Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.systemui.statusbar.policy;
     18 
     19 import android.animation.Animator;
     20 import android.animation.ObjectAnimator;
     21 import android.app.ActivityManager;
     22 import android.content.Context;
     23 import android.content.res.TypedArray;
     24 import android.hardware.input.InputManager;
     25 import android.media.AudioManager;
     26 import android.os.Bundle;
     27 import android.os.SystemClock;
     28 import android.util.AttributeSet;
     29 import android.util.Log;
     30 import android.view.HapticFeedbackConstants;
     31 import android.view.InputDevice;
     32 import android.view.KeyCharacterMap;
     33 import android.view.KeyEvent;
     34 import android.view.MotionEvent;
     35 import android.view.SoundEffectConstants;
     36 import android.view.View;
     37 import android.view.ViewConfiguration;
     38 import android.view.accessibility.AccessibilityEvent;
     39 import android.view.accessibility.AccessibilityNodeInfo;
     40 import android.widget.ImageView;
     41 
     42 import com.android.systemui.R;
     43 
     44 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
     45 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
     46 
     47 public class KeyButtonView extends ImageView {
     48     private static final String TAG = "StatusBar.KeyButtonView";
     49     private static final boolean DEBUG = false;
     50 
     51     // TODO: Get rid of this
     52     public static final float DEFAULT_QUIESCENT_ALPHA = 1f;
     53 
     54     private long mDownTime;
     55     private int mCode;
     56     private int mTouchSlop;
     57     private float mDrawingAlpha = 1f;
     58     private float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
     59     private boolean mSupportsLongpress = true;
     60     private AudioManager mAudioManager;
     61     private Animator mAnimateToQuiescent = new ObjectAnimator();
     62 
     63     private final Runnable mCheckLongPress = new Runnable() {
     64         public void run() {
     65             if (isPressed()) {
     66                 // Log.d("KeyButtonView", "longpressed: " + this);
     67                 if (isLongClickable()) {
     68                     // Just an old-fashioned ImageView
     69                     performLongClick();
     70                 } else {
     71                     sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
     72                     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
     73                 }
     74             }
     75         }
     76     };
     77 
     78     public KeyButtonView(Context context, AttributeSet attrs) {
     79         this(context, attrs, 0);
     80     }
     81 
     82     public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
     83         super(context, attrs);
     84 
     85         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
     86                 defStyle, 0);
     87 
     88         mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
     89 
     90         mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
     91 
     92 
     93         setDrawingAlpha(mQuiescentAlpha);
     94 
     95         a.recycle();
     96 
     97         setClickable(true);
     98         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     99         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    100         setBackground(new KeyButtonRipple(context, this));
    101     }
    102 
    103     @Override
    104     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    105         super.onInitializeAccessibilityNodeInfo(info);
    106         if (mCode != 0) {
    107             info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null));
    108             if (mSupportsLongpress) {
    109                 info.addAction(
    110                         new AccessibilityNodeInfo.AccessibilityAction(ACTION_LONG_CLICK, null));
    111             }
    112         }
    113     }
    114 
    115     @Override
    116     protected void onWindowVisibilityChanged(int visibility) {
    117         super.onWindowVisibilityChanged(visibility);
    118         if (visibility != View.VISIBLE) {
    119             jumpDrawablesToCurrentState();
    120         }
    121     }
    122 
    123     @Override
    124     public boolean performAccessibilityAction(int action, Bundle arguments) {
    125         if (action == ACTION_CLICK && mCode != 0) {
    126             sendEvent(KeyEvent.ACTION_DOWN, 0, SystemClock.uptimeMillis());
    127             sendEvent(KeyEvent.ACTION_UP, 0);
    128             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    129             playSoundEffect(SoundEffectConstants.CLICK);
    130             return true;
    131         } else if (action == ACTION_LONG_CLICK && mCode != 0 && mSupportsLongpress) {
    132             sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
    133             sendEvent(KeyEvent.ACTION_UP, 0);
    134             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
    135             return true;
    136         }
    137         return super.performAccessibilityAction(action, arguments);
    138     }
    139 
    140     public void setQuiescentAlpha(float alpha, boolean animate) {
    141         mAnimateToQuiescent.cancel();
    142         alpha = Math.min(Math.max(alpha, 0), 1);
    143         if (alpha == mQuiescentAlpha && alpha == mDrawingAlpha) return;
    144         mQuiescentAlpha = alpha;
    145         if (DEBUG) Log.d(TAG, "New quiescent alpha = " + mQuiescentAlpha);
    146         if (animate) {
    147             mAnimateToQuiescent = animateToQuiescent();
    148             mAnimateToQuiescent.start();
    149         } else {
    150             setDrawingAlpha(mQuiescentAlpha);
    151         }
    152     }
    153 
    154     private ObjectAnimator animateToQuiescent() {
    155         return ObjectAnimator.ofFloat(this, "drawingAlpha", mQuiescentAlpha);
    156     }
    157 
    158     public float getQuiescentAlpha() {
    159         return mQuiescentAlpha;
    160     }
    161 
    162     public float getDrawingAlpha() {
    163         return mDrawingAlpha;
    164     }
    165 
    166     public void setDrawingAlpha(float x) {
    167         setImageAlpha((int) (x * 255));
    168         mDrawingAlpha = x;
    169     }
    170 
    171     public boolean onTouchEvent(MotionEvent ev) {
    172         final int action = ev.getAction();
    173         int x, y;
    174 
    175         switch (action) {
    176             case MotionEvent.ACTION_DOWN:
    177                 //Log.d("KeyButtonView", "press");
    178                 mDownTime = SystemClock.uptimeMillis();
    179                 setPressed(true);
    180                 if (mCode != 0) {
    181                     sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
    182                 } else {
    183                     // Provide the same haptic feedback that the system offers for virtual keys.
    184                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
    185                 }
    186                 if (mSupportsLongpress) {
    187                     removeCallbacks(mCheckLongPress);
    188                     postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
    189                 }
    190                 break;
    191             case MotionEvent.ACTION_MOVE:
    192                 x = (int)ev.getX();
    193                 y = (int)ev.getY();
    194                 setPressed(x >= -mTouchSlop
    195                         && x < getWidth() + mTouchSlop
    196                         && y >= -mTouchSlop
    197                         && y < getHeight() + mTouchSlop);
    198                 break;
    199             case MotionEvent.ACTION_CANCEL:
    200                 setPressed(false);
    201                 if (mCode != 0) {
    202                     sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
    203                 }
    204                 if (mSupportsLongpress) {
    205                     removeCallbacks(mCheckLongPress);
    206                 }
    207                 break;
    208             case MotionEvent.ACTION_UP:
    209                 final boolean doIt = isPressed();
    210                 setPressed(false);
    211                 if (mCode != 0) {
    212                     if (doIt) {
    213                         sendEvent(KeyEvent.ACTION_UP, 0);
    214                         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    215                         playSoundEffect(SoundEffectConstants.CLICK);
    216                     } else {
    217                         sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
    218                     }
    219                 } else {
    220                     // no key code, just a regular ImageView
    221                     if (doIt) {
    222                         performClick();
    223                     }
    224                 }
    225                 if (mSupportsLongpress) {
    226                     removeCallbacks(mCheckLongPress);
    227                 }
    228                 break;
    229         }
    230 
    231         return true;
    232     }
    233 
    234     public void playSoundEffect(int soundConstant) {
    235         mAudioManager.playSoundEffect(soundConstant, ActivityManager.getCurrentUser());
    236     };
    237 
    238     public void sendEvent(int action, int flags) {
    239         sendEvent(action, flags, SystemClock.uptimeMillis());
    240     }
    241 
    242     void sendEvent(int action, int flags, long when) {
    243         final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
    244         final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
    245                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
    246                 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
    247                 InputDevice.SOURCE_KEYBOARD);
    248         InputManager.getInstance().injectInputEvent(ev,
    249                 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
    250     }
    251 }
    252 
    253 
    254