blob: a00fab35185f45bacadd4e7a5a6314dfa44cfb12 [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;
Justin Hobceff712011-10-26 21:01:13 -040049 final float BUTTON_QUIESCENT_ALPHA = 0.6f;
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;
55 float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
Daniel Sandler44a46162011-08-09 16:48:21 -040056 boolean mSupportsLongpress = true;
Daniel Sandlerc638c1e2011-08-24 16:19:23 -070057 RectF mRect = new RectF(0f,0f,0f,0f);
Michael Jurka60bc69e2012-01-16 05:05:56 -080058 AnimatorSet mPressedAnim;
Daniel Sandleraa051d62011-03-01 16:23:57 -050059
Daniel Sandler804eb852010-08-31 15:43:50 -040060 Runnable mCheckLongPress = new Runnable() {
61 public void run() {
Daniel Sandler804eb852010-08-31 15:43:50 -040062 if (isPressed()) {
Daniel Sandler44a46162011-08-09 16:48:21 -040063 // Slog.d("KeyButtonView", "longpressed: " + this);
Daniel Sandlera375c942011-07-29 00:33:53 -040064 if (mCode != 0) {
Jeff Brown98392ef2011-09-12 18:24:59 -070065 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
Daniel Sandlera375c942011-07-29 00:33:53 -040066 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
67 } else {
68 // Just an old-fashioned ImageView
69 performLongClick();
70 }
Daniel Sandler804eb852010-08-31 15:43:50 -040071 }
72 }
73 };
Joe Onorato86f9bd22010-06-30 17:03:42 -040074
75 public KeyButtonView(Context context, AttributeSet attrs) {
76 this(context, attrs, 0);
77 }
78
79 public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
80 super(context, attrs);
81
82 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
83 defStyle, 0);
84
85 mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
Daniel Sandler44a46162011-08-09 16:48:21 -040086
87 mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
Daniel Sandlera375c942011-07-29 00:33:53 -040088
89 mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
90 if (mGlowBG != null) {
Michael Jurka0b658662012-01-12 05:48:19 -080091 setDrawingAlpha(BUTTON_QUIESCENT_ALPHA);
Joe Onorato86f9bd22010-06-30 17:03:42 -040092 }
93
94 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();
106 canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
107 mGlowBG.setBounds(0, 0, w, h);
Michael Jurka0b658662012-01-12 05:48:19 -0800108 mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
Daniel Sandlera375c942011-07-29 00:33:53 -0400109 mGlowBG.draw(canvas);
110 canvas.restore();
Daniel Sandlerc638c1e2011-08-24 16:19:23 -0700111 mRect.right = w;
112 mRect.bottom = h;
Daniel Sandlera375c942011-07-29 00:33:53 -0400113 }
114 super.onDraw(canvas);
Daniel Sandlera375c942011-07-29 00:33:53 -0400115 }
116
117 public float getDrawingAlpha() {
118 if (mGlowBG == null) return 0;
119 return mDrawingAlpha;
120 }
121
122 public void setDrawingAlpha(float x) {
123 if (mGlowBG == null) return;
Michael Jurka0b658662012-01-12 05:48:19 -0800124 // Calling setAlpha(int), which is an ImageView-specific
125 // method that's different from setAlpha(float). This sets
126 // the alpha on this ImageView's drawable directly
127 setAlpha((int) (x * 255));
Daniel Sandlera375c942011-07-29 00:33:53 -0400128 mDrawingAlpha = x;
Daniel Sandlera375c942011-07-29 00:33:53 -0400129 }
130
131 public float getGlowAlpha() {
132 if (mGlowBG == null) return 0;
133 return mGlowAlpha;
134 }
135
136 public void setGlowAlpha(float x) {
137 if (mGlowBG == null) return;
138 mGlowAlpha = x;
139 invalidate();
140 }
141
142 public float getGlowScale() {
143 if (mGlowBG == null) return 0;
144 return mGlowScale;
145 }
146
147 public void setGlowScale(float x) {
148 if (mGlowBG == null) return;
149 mGlowScale = x;
150 final float w = getWidth();
151 final float h = getHeight();
Daniel Sandler96f48182011-08-17 09:50:35 -0400152 if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
153 // this only works if we know the glow will never leave our bounds
Daniel Sandlera375c942011-07-29 00:33:53 -0400154 invalidate();
155 } else {
Daniel Sandler96f48182011-08-17 09:50:35 -0400156 final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
157 final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
Daniel Sandlera375c942011-07-29 00:33:53 -0400158 com.android.systemui.SwipeHelper.invalidateGlobalRegion(
159 this,
Daniel Sandler52985822011-07-29 08:42:15 -0400160 new RectF(getLeft() - rx,
161 getTop() - ry,
162 getRight() + rx,
163 getBottom() + ry));
Daniel Sandler96f48182011-08-17 09:50:35 -0400164
165 // also invalidate our immediate parent to help avoid situations where nearby glows
166 // interfere
167 ((View)getParent()).invalidate();
Daniel Sandlera375c942011-07-29 00:33:53 -0400168 }
169 }
170
171 public void setPressed(boolean pressed) {
172 if (mGlowBG != null) {
173 if (pressed != isPressed()) {
Michael Jurka60bc69e2012-01-16 05:05:56 -0800174 if (mPressedAnim != null && mPressedAnim.isRunning()) {
175 mPressedAnim.cancel();
176 }
177 final AnimatorSet as = mPressedAnim = new AnimatorSet();
Daniel Sandlera375c942011-07-29 00:33:53 -0400178 if (pressed) {
Justin Hobceff712011-10-26 21:01:13 -0400179 if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
180 mGlowScale = GLOW_MAX_SCALE_FACTOR;
181 if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA)
182 mGlowAlpha = BUTTON_QUIESCENT_ALPHA;
Daniel Sandlera375c942011-07-29 00:33:53 -0400183 setDrawingAlpha(1f);
184 as.playTogether(
185 ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
Daniel Sandler96f48182011-08-17 09:50:35 -0400186 ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR)
Daniel Sandlera375c942011-07-29 00:33:53 -0400187 );
188 as.setDuration(50);
189 } else {
190 as.playTogether(
191 ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
192 ObjectAnimator.ofFloat(this, "glowScale", 1f),
Justin Hobceff712011-10-26 21:01:13 -0400193 ObjectAnimator.ofFloat(this, "drawingAlpha", BUTTON_QUIESCENT_ALPHA)
Daniel Sandlera375c942011-07-29 00:33:53 -0400194 );
195 as.setDuration(500);
196 }
197 as.start();
198 }
199 }
200 super.setPressed(pressed);
201 }
202
Joe Onorato86f9bd22010-06-30 17:03:42 -0400203 public boolean onTouchEvent(MotionEvent ev) {
204 final int action = ev.getAction();
205 int x, y;
206
207 switch (action) {
208 case MotionEvent.ACTION_DOWN:
Christopher Tate7a1a2f02010-09-09 12:22:40 -0700209 //Slog.d("KeyButtonView", "press");
Joe Onorato86f9bd22010-06-30 17:03:42 -0400210 mDownTime = SystemClock.uptimeMillis();
Daniel Sandler44a46162011-08-09 16:48:21 -0400211 setPressed(true);
Jeff Brown98392ef2011-09-12 18:24:59 -0700212 if (mCode != 0) {
213 sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
214 } else {
215 // Provide the same haptic feedback that the system offers for virtual keys.
216 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
217 }
Daniel Sandler44a46162011-08-09 16:48:21 -0400218 if (mSupportsLongpress) {
219 removeCallbacks(mCheckLongPress);
220 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
Daniel Sandler44a46162011-08-09 16:48:21 -0400221 }
Joe Onorato86f9bd22010-06-30 17:03:42 -0400222 break;
223 case MotionEvent.ACTION_MOVE:
Jeff Brown98392ef2011-09-12 18:24:59 -0700224 x = (int)ev.getX();
225 y = (int)ev.getY();
226 setPressed(x >= -mTouchSlop
227 && x < getWidth() + mTouchSlop
228 && y >= -mTouchSlop
229 && y < getHeight() + mTouchSlop);
Joe Onoratoabb27772010-11-15 13:30:12 -0800230 break;
231 case MotionEvent.ACTION_CANCEL:
232 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700233 if (mCode != 0) {
234 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
235 }
236 if (mSupportsLongpress) {
237 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400238 }
239 break;
240 case MotionEvent.ACTION_UP:
Daniel Sandleraa051d62011-03-01 16:23:57 -0500241 final boolean doIt = isPressed();
Daniel Sandler804eb852010-08-31 15:43:50 -0400242 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700243 if (mCode != 0) {
244 if (doIt) {
245 sendEvent(KeyEvent.ACTION_UP, 0);
246 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
247 playSoundEffect(SoundEffectConstants.CLICK);
Patrick Dubroy5ee1a3d2011-03-02 19:11:19 -0800248 } else {
Jeff Brown98392ef2011-09-12 18:24:59 -0700249 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
Daniel Sandleraa051d62011-03-01 16:23:57 -0500250 }
Jeff Brown98392ef2011-09-12 18:24:59 -0700251 } else {
252 // no key code, just a regular ImageView
253 if (doIt) {
254 performClick();
255 }
256 }
257 if (mSupportsLongpress) {
258 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400259 }
260 break;
261 }
262
263 return true;
264 }
265
Jeff Brownbbda99d2010-07-28 15:48:59 -0700266 void sendEvent(int action, int flags) {
267 sendEvent(action, flags, SystemClock.uptimeMillis());
Joe Onorato86f9bd22010-06-30 17:03:42 -0400268 }
269
Jeff Brownbbda99d2010-07-28 15:48:59 -0700270 void sendEvent(int action, int flags, long when) {
Jeff Brown98392ef2011-09-12 18:24:59 -0700271 final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
272 final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
273 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
274 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
275 InputDevice.SOURCE_KEYBOARD);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700276 InputManager.getInstance().injectInputEvent(ev,
277 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400278 }
279}
280
281