blob: f32595767497ecbc601cee6a4fb74de87c37273e [file] [log] [blame]
Joe Onorato86f9bd22010-06-30 17:03:42 -04001/*
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
Joe Onoratofd52b182010-11-10 18:00:52 -080017package com.android.systemui.statusbar.policy;
Joe Onorato86f9bd22010-06-30 17:03:42 -040018
Daniel Sandlera375c942011-07-29 00:33:53 -040019import android.animation.AnimatorSet;
20import android.animation.ObjectAnimator;
Joe Onorato86f9bd22010-06-30 17:03:42 -040021import android.content.Context;
22import android.content.res.TypedArray;
Daniel Sandlera375c942011-07-29 00:33:53 -040023import android.graphics.Canvas;
24import android.graphics.RectF;
John Spurlockde84f0e2013-06-12 12:41:00 -040025import android.graphics.drawable.Drawable;
Jeff Brownac143512012-04-05 18:57:33 -070026import android.hardware.input.InputManager;
Joe Onorato86f9bd22010-06-30 17:03:42 -040027import android.os.SystemClock;
Joe Onorato86f9bd22010-06-30 17:03:42 -040028import android.util.AttributeSet;
Daniel Sandler804eb852010-08-31 15:43:50 -040029import android.view.HapticFeedbackConstants;
Jeff Brownbbda99d2010-07-28 15:48:59 -070030import android.view.InputDevice;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080031import android.view.KeyCharacterMap;
Joe Onorato86f9bd22010-06-30 17:03:42 -040032import android.view.KeyEvent;
33import android.view.MotionEvent;
Daniel Sandleraa051d62011-03-01 16:23:57 -050034import android.view.SoundEffectConstants;
Daniel Sandlera375c942011-07-29 00:33:53 -040035import android.view.View;
Daniel Sandler804eb852010-08-31 15:43:50 -040036import android.view.ViewConfiguration;
John Spurlockde84f0e2013-06-12 12:41:00 -040037import android.view.accessibility.AccessibilityEvent;
Joe Onorato86f9bd22010-06-30 17:03:42 -040038import android.widget.ImageView;
Joe Onorato86f9bd22010-06-30 17:03:42 -040039
40import com.android.systemui.R;
41
42public class KeyButtonView extends ImageView {
Joe Onoratofd52b182010-11-10 18:00:52 -080043 private static final String TAG = "StatusBar.KeyButtonView";
44
Daniel Sandler96f48182011-08-17 09:50:35 -040045 final float GLOW_MAX_SCALE_FACTOR = 1.8f;
Christian Robertson9345b122012-06-15 19:36:30 -070046 final float BUTTON_QUIESCENT_ALPHA = 0.70f;
Daniel Sandler96f48182011-08-17 09:50:35 -040047
Joe Onorato86f9bd22010-06-30 17:03:42 -040048 long mDownTime;
Joe Onorato86f9bd22010-06-30 17:03:42 -040049 int mCode;
Daniel Sandleraa051d62011-03-01 16:23:57 -050050 int mTouchSlop;
Daniel Sandlera375c942011-07-29 00:33:53 -040051 Drawable mGlowBG;
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -040052 int mGlowWidth, mGlowHeight;
Daniel Sandlera375c942011-07-29 00:33:53 -040053 float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
Daniel Sandler44a46162011-08-09 16:48:21 -040054 boolean mSupportsLongpress = true;
Daniel Sandlerc638c1e2011-08-24 16:19:23 -070055 RectF mRect = new RectF(0f,0f,0f,0f);
Michael Jurka60bc69e2012-01-16 05:05:56 -080056 AnimatorSet mPressedAnim;
Daniel Sandleraa051d62011-03-01 16:23:57 -050057
Daniel Sandler804eb852010-08-31 15:43:50 -040058 Runnable mCheckLongPress = new Runnable() {
59 public void run() {
Daniel Sandler804eb852010-08-31 15:43:50 -040060 if (isPressed()) {
John Spurlockcd686b52013-06-05 10:13:46 -040061 // Log.d("KeyButtonView", "longpressed: " + this);
Daniel Sandlera375c942011-07-29 00:33:53 -040062 if (mCode != 0) {
Jeff Brown98392ef2011-09-12 18:24:59 -070063 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
Daniel Sandlera375c942011-07-29 00:33:53 -040064 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
65 } else {
66 // Just an old-fashioned ImageView
67 performLongClick();
68 }
Daniel Sandler804eb852010-08-31 15:43:50 -040069 }
70 }
71 };
Joe Onorato86f9bd22010-06-30 17:03:42 -040072
73 public KeyButtonView(Context context, AttributeSet attrs) {
74 this(context, attrs, 0);
75 }
76
77 public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
78 super(context, attrs);
79
80 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
81 defStyle, 0);
82
83 mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
John Spurlock209bede2013-07-17 12:23:27 -040084
Daniel Sandler44a46162011-08-09 16:48:21 -040085 mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
Daniel Sandlera375c942011-07-29 00:33:53 -040086
87 mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
88 if (mGlowBG != null) {
Michael Jurka0b658662012-01-12 05:48:19 -080089 setDrawingAlpha(BUTTON_QUIESCENT_ALPHA);
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -040090 mGlowWidth = mGlowBG.getIntrinsicWidth();
91 mGlowHeight = mGlowBG.getIntrinsicHeight();
Joe Onorato86f9bd22010-06-30 17:03:42 -040092 }
John Spurlock209bede2013-07-17 12:23:27 -040093
Joe Onorato86f9bd22010-06-30 17:03:42 -040094 a.recycle();
95
Joe Onorato641bad42011-01-06 15:54:23 -080096 setClickable(true);
Daniel Sandleraa051d62011-03-01 16:23:57 -050097 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
Joe Onorato86f9bd22010-06-30 17:03:42 -040098 }
99
Daniel Sandlera375c942011-07-29 00:33:53 -0400100 @Override
101 protected void onDraw(Canvas canvas) {
102 if (mGlowBG != null) {
103 canvas.save();
104 final int w = getWidth();
105 final int h = getHeight();
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -0400106 final float aspect = (float)mGlowWidth / mGlowHeight;
107 final int drawW = (int)(h*aspect);
108 final int drawH = h;
109 final int margin = (drawW-w)/2;
Daniel Sandlera375c942011-07-29 00:33:53 -0400110 canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -0400111 mGlowBG.setBounds(-margin, 0, drawW-margin, drawH);
Michael Jurka0b658662012-01-12 05:48:19 -0800112 mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
Daniel Sandlera375c942011-07-29 00:33:53 -0400113 mGlowBG.draw(canvas);
114 canvas.restore();
Daniel Sandlerc638c1e2011-08-24 16:19:23 -0700115 mRect.right = w;
116 mRect.bottom = h;
Daniel Sandlera375c942011-07-29 00:33:53 -0400117 }
118 super.onDraw(canvas);
Daniel Sandlera375c942011-07-29 00:33:53 -0400119 }
120
121 public float getDrawingAlpha() {
122 if (mGlowBG == null) return 0;
123 return mDrawingAlpha;
124 }
125
126 public void setDrawingAlpha(float x) {
127 if (mGlowBG == null) return;
Michael Jurka0b658662012-01-12 05:48:19 -0800128 // Calling setAlpha(int), which is an ImageView-specific
129 // method that's different from setAlpha(float). This sets
130 // the alpha on this ImageView's drawable directly
131 setAlpha((int) (x * 255));
Daniel Sandlera375c942011-07-29 00:33:53 -0400132 mDrawingAlpha = x;
Daniel Sandlera375c942011-07-29 00:33:53 -0400133 }
134
135 public float getGlowAlpha() {
136 if (mGlowBG == null) return 0;
137 return mGlowAlpha;
138 }
139
140 public void setGlowAlpha(float x) {
141 if (mGlowBG == null) return;
142 mGlowAlpha = x;
143 invalidate();
144 }
145
146 public float getGlowScale() {
147 if (mGlowBG == null) return 0;
148 return mGlowScale;
149 }
150
151 public void setGlowScale(float x) {
152 if (mGlowBG == null) return;
153 mGlowScale = x;
154 final float w = getWidth();
155 final float h = getHeight();
Daniel Sandler96f48182011-08-17 09:50:35 -0400156 if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
157 // this only works if we know the glow will never leave our bounds
Daniel Sandlera375c942011-07-29 00:33:53 -0400158 invalidate();
159 } else {
Daniel Sandler96f48182011-08-17 09:50:35 -0400160 final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
161 final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
Daniel Sandlera375c942011-07-29 00:33:53 -0400162 com.android.systemui.SwipeHelper.invalidateGlobalRegion(
163 this,
Daniel Sandler52985822011-07-29 08:42:15 -0400164 new RectF(getLeft() - rx,
165 getTop() - ry,
166 getRight() + rx,
167 getBottom() + ry));
Daniel Sandler96f48182011-08-17 09:50:35 -0400168
169 // also invalidate our immediate parent to help avoid situations where nearby glows
170 // interfere
171 ((View)getParent()).invalidate();
Daniel Sandlera375c942011-07-29 00:33:53 -0400172 }
173 }
174
175 public void setPressed(boolean pressed) {
176 if (mGlowBG != null) {
177 if (pressed != isPressed()) {
Michael Jurka60bc69e2012-01-16 05:05:56 -0800178 if (mPressedAnim != null && mPressedAnim.isRunning()) {
179 mPressedAnim.cancel();
180 }
181 final AnimatorSet as = mPressedAnim = new AnimatorSet();
Daniel Sandlera375c942011-07-29 00:33:53 -0400182 if (pressed) {
John Spurlock209bede2013-07-17 12:23:27 -0400183 if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
Justin Hobceff712011-10-26 21:01:13 -0400184 mGlowScale = GLOW_MAX_SCALE_FACTOR;
185 if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA)
186 mGlowAlpha = BUTTON_QUIESCENT_ALPHA;
Daniel Sandlera375c942011-07-29 00:33:53 -0400187 setDrawingAlpha(1f);
188 as.playTogether(
189 ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
Daniel Sandler96f48182011-08-17 09:50:35 -0400190 ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR)
Daniel Sandlera375c942011-07-29 00:33:53 -0400191 );
192 as.setDuration(50);
193 } else {
194 as.playTogether(
195 ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
196 ObjectAnimator.ofFloat(this, "glowScale", 1f),
Justin Hobceff712011-10-26 21:01:13 -0400197 ObjectAnimator.ofFloat(this, "drawingAlpha", BUTTON_QUIESCENT_ALPHA)
Daniel Sandlera375c942011-07-29 00:33:53 -0400198 );
199 as.setDuration(500);
200 }
201 as.start();
202 }
203 }
204 super.setPressed(pressed);
205 }
206
Joe Onorato86f9bd22010-06-30 17:03:42 -0400207 public boolean onTouchEvent(MotionEvent ev) {
208 final int action = ev.getAction();
209 int x, y;
210
211 switch (action) {
212 case MotionEvent.ACTION_DOWN:
John Spurlockcd686b52013-06-05 10:13:46 -0400213 //Log.d("KeyButtonView", "press");
Joe Onorato86f9bd22010-06-30 17:03:42 -0400214 mDownTime = SystemClock.uptimeMillis();
Daniel Sandler44a46162011-08-09 16:48:21 -0400215 setPressed(true);
Jeff Brown98392ef2011-09-12 18:24:59 -0700216 if (mCode != 0) {
217 sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
218 } else {
219 // Provide the same haptic feedback that the system offers for virtual keys.
220 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
221 }
Daniel Sandler44a46162011-08-09 16:48:21 -0400222 if (mSupportsLongpress) {
223 removeCallbacks(mCheckLongPress);
224 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
Daniel Sandler44a46162011-08-09 16:48:21 -0400225 }
Joe Onorato86f9bd22010-06-30 17:03:42 -0400226 break;
227 case MotionEvent.ACTION_MOVE:
Jeff Brown98392ef2011-09-12 18:24:59 -0700228 x = (int)ev.getX();
229 y = (int)ev.getY();
230 setPressed(x >= -mTouchSlop
231 && x < getWidth() + mTouchSlop
232 && y >= -mTouchSlop
233 && y < getHeight() + mTouchSlop);
Joe Onoratoabb27772010-11-15 13:30:12 -0800234 break;
235 case MotionEvent.ACTION_CANCEL:
236 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700237 if (mCode != 0) {
238 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
239 }
240 if (mSupportsLongpress) {
241 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400242 }
243 break;
244 case MotionEvent.ACTION_UP:
Daniel Sandleraa051d62011-03-01 16:23:57 -0500245 final boolean doIt = isPressed();
Daniel Sandler804eb852010-08-31 15:43:50 -0400246 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700247 if (mCode != 0) {
248 if (doIt) {
249 sendEvent(KeyEvent.ACTION_UP, 0);
250 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
251 playSoundEffect(SoundEffectConstants.CLICK);
Patrick Dubroy5ee1a3d2011-03-02 19:11:19 -0800252 } else {
Jeff Brown98392ef2011-09-12 18:24:59 -0700253 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
Daniel Sandleraa051d62011-03-01 16:23:57 -0500254 }
Jeff Brown98392ef2011-09-12 18:24:59 -0700255 } else {
256 // no key code, just a regular ImageView
257 if (doIt) {
258 performClick();
259 }
260 }
261 if (mSupportsLongpress) {
262 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400263 }
264 break;
265 }
266
267 return true;
268 }
269
Jeff Brownbbda99d2010-07-28 15:48:59 -0700270 void sendEvent(int action, int flags) {
271 sendEvent(action, flags, SystemClock.uptimeMillis());
Joe Onorato86f9bd22010-06-30 17:03:42 -0400272 }
273
Jeff Brownbbda99d2010-07-28 15:48:59 -0700274 void sendEvent(int action, int flags, long when) {
Jeff Brown98392ef2011-09-12 18:24:59 -0700275 final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
276 final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
277 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
278 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
279 InputDevice.SOURCE_KEYBOARD);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700280 InputManager.getInstance().injectInputEvent(ev,
281 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400282 }
283}
284
285