• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
KeyButtonView(Context context, AttributeSet attrs)76     public KeyButtonView(Context context, AttributeSet attrs) {
77         this(context, attrs, 0);
78     }
79 
KeyButtonView(Context context, AttributeSet attrs, int defStyle)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
onDraw(Canvas canvas)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 
getDrawingAlpha()124     public float getDrawingAlpha() {
125         if (mGlowBG == null) return 0;
126         return mDrawingAlpha;
127     }
128 
setDrawingAlpha(float x)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 
getGlowAlpha()138     public float getGlowAlpha() {
139         if (mGlowBG == null) return 0;
140         return mGlowAlpha;
141     }
142 
setGlowAlpha(float x)143     public void setGlowAlpha(float x) {
144         if (mGlowBG == null) return;
145         mGlowAlpha = x;
146         invalidate();
147     }
148 
getGlowScale()149     public float getGlowScale() {
150         if (mGlowBG == null) return 0;
151         return mGlowScale;
152     }
153 
setGlowScale(float x)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 
setPressed(boolean pressed)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 
onTouchEvent(MotionEvent ev)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 
sendEvent(int action, int flags)273     void sendEvent(int action, int flags) {
274         sendEvent(action, flags, SystemClock.uptimeMillis());
275     }
276 
sendEvent(int action, int flags, long when)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