blob: 82d6a990928c42fd417dfdb747107731afc999fa [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;
Joe Onorato86f9bd22010-06-30 17:03:42 -040023import android.graphics.drawable.Drawable;
Daniel Sandlera375c942011-07-29 00:33:53 -040024import android.graphics.Canvas;
25import android.graphics.RectF;
Jeff Brownac143512012-04-05 18:57:33 -070026import android.hardware.input.InputManager;
Joe Onorato86f9bd22010-06-30 17:03:42 -040027import android.os.RemoteException;
28import android.os.SystemClock;
29import android.os.ServiceManager;
30import android.util.AttributeSet;
Daniel Sandleraa051d62011-03-01 16:23:57 -050031import android.view.accessibility.AccessibilityEvent;
Daniel Sandler804eb852010-08-31 15:43:50 -040032import android.view.HapticFeedbackConstants;
Joe Onorato86f9bd22010-06-30 17:03:42 -040033import android.view.IWindowManager;
Jeff Brownbbda99d2010-07-28 15:48:59 -070034import android.view.InputDevice;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080035import android.view.KeyCharacterMap;
Joe Onorato86f9bd22010-06-30 17:03:42 -040036import android.view.KeyEvent;
37import android.view.MotionEvent;
Daniel Sandleraa051d62011-03-01 16:23:57 -050038import android.view.SoundEffectConstants;
Daniel Sandlera375c942011-07-29 00:33:53 -040039import android.view.View;
Daniel Sandler804eb852010-08-31 15:43:50 -040040import android.view.ViewConfiguration;
Joe Onorato86f9bd22010-06-30 17:03:42 -040041import android.widget.ImageView;
Joe Onorato86f9bd22010-06-30 17:03:42 -040042
43import com.android.systemui.R;
44
45public class KeyButtonView extends ImageView {
Joe Onoratofd52b182010-11-10 18:00:52 -080046 private static final String TAG = "StatusBar.KeyButtonView";
47
Daniel Sandler96f48182011-08-17 09:50:35 -040048 final float GLOW_MAX_SCALE_FACTOR = 1.8f;
Christian Robertson9345b122012-06-15 19:36:30 -070049 final float BUTTON_QUIESCENT_ALPHA = 0.70f;
Daniel Sandler96f48182011-08-17 09:50:35 -040050
Joe Onorato86f9bd22010-06-30 17:03:42 -040051 long mDownTime;
Joe Onorato86f9bd22010-06-30 17:03:42 -040052 int mCode;
Daniel Sandleraa051d62011-03-01 16:23:57 -050053 int mTouchSlop;
Daniel Sandlera375c942011-07-29 00:33:53 -040054 Drawable mGlowBG;
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -040055 int mGlowWidth, mGlowHeight;
Daniel Sandlera375c942011-07-29 00:33:53 -040056 float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
Daniel Sandler44a46162011-08-09 16:48:21 -040057 boolean mSupportsLongpress = true;
Daniel Sandlerc638c1e2011-08-24 16:19:23 -070058 RectF mRect = new RectF(0f,0f,0f,0f);
Michael Jurka60bc69e2012-01-16 05:05:56 -080059 AnimatorSet mPressedAnim;
Daniel Sandleraa051d62011-03-01 16:23:57 -050060
Daniel Sandler804eb852010-08-31 15:43:50 -040061 Runnable mCheckLongPress = new Runnable() {
62 public void run() {
Daniel Sandler804eb852010-08-31 15:43:50 -040063 if (isPressed()) {
Daniel Sandler44a46162011-08-09 16:48:21 -040064 // Slog.d("KeyButtonView", "longpressed: " + this);
Daniel Sandlera375c942011-07-29 00:33:53 -040065 if (mCode != 0) {
Jeff Brown98392ef2011-09-12 18:24:59 -070066 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
Daniel Sandlera375c942011-07-29 00:33:53 -040067 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
68 } else {
69 // Just an old-fashioned ImageView
70 performLongClick();
71 }
Daniel Sandler804eb852010-08-31 15:43:50 -040072 }
73 }
74 };
Joe Onorato86f9bd22010-06-30 17:03:42 -040075
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);
Daniel Sandler44a46162011-08-09 16:48:21 -040087
88 mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
Daniel Sandlera375c942011-07-29 00:33:53 -040089
90 mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
91 if (mGlowBG != null) {
Michael Jurka0b658662012-01-12 05:48:19 -080092 setDrawingAlpha(BUTTON_QUIESCENT_ALPHA);
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -040093 mGlowWidth = mGlowBG.getIntrinsicWidth();
94 mGlowHeight = mGlowBG.getIntrinsicHeight();
Joe Onorato86f9bd22010-06-30 17:03:42 -040095 }
96
97 a.recycle();
98
Joe Onorato641bad42011-01-06 15:54:23 -080099 setClickable(true);
Daniel Sandleraa051d62011-03-01 16:23:57 -0500100 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
Joe Onorato86f9bd22010-06-30 17:03:42 -0400101 }
102
Daniel Sandlera375c942011-07-29 00:33:53 -0400103 @Override
104 protected void onDraw(Canvas canvas) {
105 if (mGlowBG != null) {
106 canvas.save();
107 final int w = getWidth();
108 final int h = getHeight();
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -0400109 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;
Daniel Sandlera375c942011-07-29 00:33:53 -0400113 canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
Daniel Sandler3fd0e1a2012-05-16 14:57:52 -0400114 mGlowBG.setBounds(-margin, 0, drawW-margin, drawH);
Michael Jurka0b658662012-01-12 05:48:19 -0800115 mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
Daniel Sandlera375c942011-07-29 00:33:53 -0400116 mGlowBG.draw(canvas);
117 canvas.restore();
Daniel Sandlerc638c1e2011-08-24 16:19:23 -0700118 mRect.right = w;
119 mRect.bottom = h;
Daniel Sandlera375c942011-07-29 00:33:53 -0400120 }
121 super.onDraw(canvas);
Daniel Sandlera375c942011-07-29 00:33:53 -0400122 }
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;
Michael Jurka0b658662012-01-12 05:48:19 -0800131 // 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));
Daniel Sandlera375c942011-07-29 00:33:53 -0400135 mDrawingAlpha = x;
Daniel Sandlera375c942011-07-29 00:33:53 -0400136 }
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();
Daniel Sandler96f48182011-08-17 09:50:35 -0400159 if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
160 // this only works if we know the glow will never leave our bounds
Daniel Sandlera375c942011-07-29 00:33:53 -0400161 invalidate();
162 } else {
Daniel Sandler96f48182011-08-17 09:50:35 -0400163 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;
Daniel Sandlera375c942011-07-29 00:33:53 -0400165 com.android.systemui.SwipeHelper.invalidateGlobalRegion(
166 this,
Daniel Sandler52985822011-07-29 08:42:15 -0400167 new RectF(getLeft() - rx,
168 getTop() - ry,
169 getRight() + rx,
170 getBottom() + ry));
Daniel Sandler96f48182011-08-17 09:50:35 -0400171
172 // also invalidate our immediate parent to help avoid situations where nearby glows
173 // interfere
174 ((View)getParent()).invalidate();
Daniel Sandlera375c942011-07-29 00:33:53 -0400175 }
176 }
177
178 public void setPressed(boolean pressed) {
179 if (mGlowBG != null) {
180 if (pressed != isPressed()) {
Michael Jurka60bc69e2012-01-16 05:05:56 -0800181 if (mPressedAnim != null && mPressedAnim.isRunning()) {
182 mPressedAnim.cancel();
183 }
184 final AnimatorSet as = mPressedAnim = new AnimatorSet();
Daniel Sandlera375c942011-07-29 00:33:53 -0400185 if (pressed) {
Justin Hobceff712011-10-26 21:01:13 -0400186 if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
187 mGlowScale = GLOW_MAX_SCALE_FACTOR;
188 if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA)
189 mGlowAlpha = BUTTON_QUIESCENT_ALPHA;
Daniel Sandlera375c942011-07-29 00:33:53 -0400190 setDrawingAlpha(1f);
191 as.playTogether(
192 ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
Daniel Sandler96f48182011-08-17 09:50:35 -0400193 ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR)
Daniel Sandlera375c942011-07-29 00:33:53 -0400194 );
195 as.setDuration(50);
196 } else {
197 as.playTogether(
198 ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
199 ObjectAnimator.ofFloat(this, "glowScale", 1f),
Justin Hobceff712011-10-26 21:01:13 -0400200 ObjectAnimator.ofFloat(this, "drawingAlpha", BUTTON_QUIESCENT_ALPHA)
Daniel Sandlera375c942011-07-29 00:33:53 -0400201 );
202 as.setDuration(500);
203 }
204 as.start();
205 }
206 }
207 super.setPressed(pressed);
208 }
209
Joe Onorato86f9bd22010-06-30 17:03:42 -0400210 public boolean onTouchEvent(MotionEvent ev) {
211 final int action = ev.getAction();
212 int x, y;
213
214 switch (action) {
215 case MotionEvent.ACTION_DOWN:
Christopher Tate7a1a2f02010-09-09 12:22:40 -0700216 //Slog.d("KeyButtonView", "press");
Joe Onorato86f9bd22010-06-30 17:03:42 -0400217 mDownTime = SystemClock.uptimeMillis();
Daniel Sandler44a46162011-08-09 16:48:21 -0400218 setPressed(true);
Jeff Brown98392ef2011-09-12 18:24:59 -0700219 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 }
Daniel Sandler44a46162011-08-09 16:48:21 -0400225 if (mSupportsLongpress) {
226 removeCallbacks(mCheckLongPress);
227 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
Daniel Sandler44a46162011-08-09 16:48:21 -0400228 }
Joe Onorato86f9bd22010-06-30 17:03:42 -0400229 break;
230 case MotionEvent.ACTION_MOVE:
Jeff Brown98392ef2011-09-12 18:24:59 -0700231 x = (int)ev.getX();
232 y = (int)ev.getY();
233 setPressed(x >= -mTouchSlop
234 && x < getWidth() + mTouchSlop
235 && y >= -mTouchSlop
236 && y < getHeight() + mTouchSlop);
Joe Onoratoabb27772010-11-15 13:30:12 -0800237 break;
238 case MotionEvent.ACTION_CANCEL:
239 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700240 if (mCode != 0) {
241 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
242 }
243 if (mSupportsLongpress) {
244 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400245 }
246 break;
247 case MotionEvent.ACTION_UP:
Daniel Sandleraa051d62011-03-01 16:23:57 -0500248 final boolean doIt = isPressed();
Daniel Sandler804eb852010-08-31 15:43:50 -0400249 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700250 if (mCode != 0) {
251 if (doIt) {
252 sendEvent(KeyEvent.ACTION_UP, 0);
253 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
254 playSoundEffect(SoundEffectConstants.CLICK);
Patrick Dubroy5ee1a3d2011-03-02 19:11:19 -0800255 } else {
Jeff Brown98392ef2011-09-12 18:24:59 -0700256 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
Daniel Sandleraa051d62011-03-01 16:23:57 -0500257 }
Jeff Brown98392ef2011-09-12 18:24:59 -0700258 } else {
259 // no key code, just a regular ImageView
260 if (doIt) {
261 performClick();
262 }
263 }
264 if (mSupportsLongpress) {
265 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400266 }
267 break;
268 }
269
270 return true;
271 }
272
Jeff Brownbbda99d2010-07-28 15:48:59 -0700273 void sendEvent(int action, int flags) {
274 sendEvent(action, flags, SystemClock.uptimeMillis());
Joe Onorato86f9bd22010-06-30 17:03:42 -0400275 }
276
Jeff Brownbbda99d2010-07-28 15:48:59 -0700277 void sendEvent(int action, int flags, long when) {
Jeff Brown98392ef2011-09-12 18:24:59 -0700278 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);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700283 InputManager.getInstance().injectInputEvent(ev,
284 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400285 }
286}
287
288