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.AnimatorSet;
     20 import android.animation.ObjectAnimator;
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.graphics.drawable.Drawable;
     24 import android.graphics.Canvas;
     25 import android.graphics.RectF;
     26 import android.hardware.input.InputManager;
     27 import android.os.RemoteException;
     28 import android.os.SystemClock;
     29 import android.os.ServiceManager;
     30 import android.util.AttributeSet;
     31 import android.view.accessibility.AccessibilityEvent;
     32 import android.view.HapticFeedbackConstants;
     33 import android.view.IWindowManager;
     34 import android.view.InputDevice;
     35 import android.view.KeyCharacterMap;
     36 import android.view.KeyEvent;
     37 import android.view.MotionEvent;
     38 import android.view.SoundEffectConstants;
     39 import android.view.View;
     40 import android.view.ViewConfiguration;
     41 import android.widget.ImageView;
     42 
     43 import com.android.systemui.R;
     44 
     45 public class KeyButtonView extends ImageView {
     46     private static final String TAG = "StatusBar.KeyButtonView";
     47 
     48     final float GLOW_MAX_SCALE_FACTOR = 1.8f;
     49     final float BUTTON_QUIESCENT_ALPHA = 0.70f;
     50 
     51     long mDownTime;
     52     int mCode;
     53     int mTouchSlop;
     54     Drawable mGlowBG;
     55     int mGlowWidth, mGlowHeight;
     56     float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
     57     boolean mSupportsLongpress = true;
     58     RectF mRect = new RectF(0f,0f,0f,0f);
     59     AnimatorSet mPressedAnim;
     60 
     61     Runnable mCheckLongPress = new Runnable() {
     62         public void run() {
     63             if (isPressed()) {
     64                 // Slog.d("KeyButtonView", "longpressed: " + this);
     65                 if (mCode != 0) {
     66                     sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
     67                     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
     68                 } else {
     69                     // Just an old-fashioned ImageView
     70                     performLongClick();
     71                 }
     72             }
     73         }
     74     };
     75 
     76     public KeyButtonView(Context context, AttributeSet attrs) {
     77         this(context, attrs, 0);
     78     }
     79 
     80     public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
     81         super(context, attrs);
     82 
     83         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
     84                 defStyle, 0);
     85 
     86         mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
     87 
     88         mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
     89 
     90         mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
     91         if (mGlowBG != null) {
     92             setDrawingAlpha(BUTTON_QUIESCENT_ALPHA);
     93             mGlowWidth = mGlowBG.getIntrinsicWidth();
     94             mGlowHeight = mGlowBG.getIntrinsicHeight();
     95         }
     96 
     97         a.recycle();
     98 
     99         setClickable(true);
    100         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    101     }
    102 
    103     @Override
    104     protected void onDraw(Canvas canvas) {
    105         if (mGlowBG != null) {
    106             canvas.save();
    107             final int w = getWidth();
    108             final int h = getHeight();
    109             final float aspect = (float)mGlowWidth / mGlowHeight;
    110             final int drawW = (int)(h*aspect);
    111             final int drawH = h;
    112             final int margin = (drawW-w)/2;
    113             canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
    114             mGlowBG.setBounds(-margin, 0, drawW-margin, drawH);
    115             mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
    116             mGlowBG.draw(canvas);
    117             canvas.restore();
    118             mRect.right = w;
    119             mRect.bottom = h;
    120         }
    121         super.onDraw(canvas);
    122     }
    123 
    124     public float getDrawingAlpha() {
    125         if (mGlowBG == null) return 0;
    126         return mDrawingAlpha;
    127     }
    128 
    129     public void setDrawingAlpha(float x) {
    130         if (mGlowBG == null) return;
    131         // Calling setAlpha(int), which is an ImageView-specific
    132         // method that's different from setAlpha(float). This sets
    133         // the alpha on this ImageView's drawable directly
    134         setAlpha((int) (x * 255));
    135         mDrawingAlpha = x;
    136     }
    137 
    138     public float getGlowAlpha() {
    139         if (mGlowBG == null) return 0;
    140         return mGlowAlpha;
    141     }
    142 
    143     public void setGlowAlpha(float x) {
    144         if (mGlowBG == null) return;
    145         mGlowAlpha = x;
    146         invalidate();
    147     }
    148 
    149     public float getGlowScale() {
    150         if (mGlowBG == null) return 0;
    151         return mGlowScale;
    152     }
    153 
    154     public void setGlowScale(float x) {
    155         if (mGlowBG == null) return;
    156         mGlowScale = x;
    157         final float w = getWidth();
    158         final float h = getHeight();
    159         if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
    160             // this only works if we know the glow will never leave our bounds
    161             invalidate();
    162         } else {
    163             final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
    164             final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
    165             com.android.systemui.SwipeHelper.invalidateGlobalRegion(
    166                     this,
    167                     new RectF(getLeft() - rx,
    168                               getTop() - ry,
    169                               getRight() + rx,
    170                               getBottom() + ry));
    171 
    172             // also invalidate our immediate parent to help avoid situations where nearby glows
    173             // interfere
    174             ((View)getParent()).invalidate();
    175         }
    176     }
    177 
    178     public void setPressed(boolean pressed) {
    179         if (mGlowBG != null) {
    180             if (pressed != isPressed()) {
    181                 if (mPressedAnim != null && mPressedAnim.isRunning()) {
    182                     mPressedAnim.cancel();
    183                 }
    184                 final AnimatorSet as = mPressedAnim = new AnimatorSet();
    185                 if (pressed) {
    186                     if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
    187                         mGlowScale = GLOW_MAX_SCALE_FACTOR;
    188                     if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA)
    189                         mGlowAlpha = BUTTON_QUIESCENT_ALPHA;
    190                     setDrawingAlpha(1f);
    191                     as.playTogether(
    192                         ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
    193                         ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR)
    194                     );
    195                     as.setDuration(50);
    196                 } else {
    197                     as.playTogether(
    198                         ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
    199                         ObjectAnimator.ofFloat(this, "glowScale", 1f),
    200                         ObjectAnimator.ofFloat(this, "drawingAlpha", BUTTON_QUIESCENT_ALPHA)
    201                     );
    202                     as.setDuration(500);
    203                 }
    204                 as.start();
    205             }
    206         }
    207         super.setPressed(pressed);
    208     }
    209 
    210     public boolean onTouchEvent(MotionEvent ev) {
    211         final int action = ev.getAction();
    212         int x, y;
    213 
    214         switch (action) {
    215             case MotionEvent.ACTION_DOWN:
    216                 //Slog.d("KeyButtonView", "press");
    217                 mDownTime = SystemClock.uptimeMillis();
    218                 setPressed(true);
    219                 if (mCode != 0) {
    220                     sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
    221                 } else {
    222                     // Provide the same haptic feedback that the system offers for virtual keys.
    223                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
    224                 }
    225                 if (mSupportsLongpress) {
    226                     removeCallbacks(mCheckLongPress);
    227                     postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
    228                 }
    229                 break;
    230             case MotionEvent.ACTION_MOVE:
    231                 x = (int)ev.getX();
    232                 y = (int)ev.getY();
    233                 setPressed(x >= -mTouchSlop
    234                         && x < getWidth() + mTouchSlop
    235                         && y >= -mTouchSlop
    236                         && y < getHeight() + mTouchSlop);
    237                 break;
    238             case MotionEvent.ACTION_CANCEL:
    239                 setPressed(false);
    240                 if (mCode != 0) {
    241                     sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
    242                 }
    243                 if (mSupportsLongpress) {
    244                     removeCallbacks(mCheckLongPress);
    245                 }
    246                 break;
    247             case MotionEvent.ACTION_UP:
    248                 final boolean doIt = isPressed();
    249                 setPressed(false);
    250                 if (mCode != 0) {
    251                     if (doIt) {
    252                         sendEvent(KeyEvent.ACTION_UP, 0);
    253                         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    254                         playSoundEffect(SoundEffectConstants.CLICK);
    255                     } else {
    256                         sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
    257                     }
    258                 } else {
    259                     // no key code, just a regular ImageView
    260                     if (doIt) {
    261                         performClick();
    262                     }
    263                 }
    264                 if (mSupportsLongpress) {
    265                     removeCallbacks(mCheckLongPress);
    266                 }
    267                 break;
    268         }
    269 
    270         return true;
    271     }
    272 
    273     void sendEvent(int action, int flags) {
    274         sendEvent(action, flags, SystemClock.uptimeMillis());
    275     }
    276 
    277     void sendEvent(int action, int flags, long when) {
    278         final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
    279         final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
    280                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
    281                 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
    282                 InputDevice.SOURCE_KEYBOARD);
    283         InputManager.getInstance().injectInputEvent(ev,
    284                 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
    285     }
    286 }
    287 
    288 
    289