blob: 975c372acb86166947e26a829523e5cc3e4f7775 [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;
Joe Onorato86f9bd22010-06-30 17:03:42 -040026import android.os.RemoteException;
27import android.os.SystemClock;
28import android.os.ServiceManager;
29import android.util.AttributeSet;
Daniel Sandleraa051d62011-03-01 16:23:57 -050030import android.view.accessibility.AccessibilityEvent;
Daniel Sandler804eb852010-08-31 15:43:50 -040031import android.view.HapticFeedbackConstants;
Joe Onorato86f9bd22010-06-30 17:03:42 -040032import android.view.IWindowManager;
Jeff Brownbbda99d2010-07-28 15:48:59 -070033import android.view.InputDevice;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080034import android.view.KeyCharacterMap;
Joe Onorato86f9bd22010-06-30 17:03:42 -040035import android.view.KeyEvent;
36import android.view.MotionEvent;
Daniel Sandleraa051d62011-03-01 16:23:57 -050037import android.view.SoundEffectConstants;
Daniel Sandlera375c942011-07-29 00:33:53 -040038import android.view.View;
Daniel Sandler804eb852010-08-31 15:43:50 -040039import android.view.ViewConfiguration;
Joe Onorato86f9bd22010-06-30 17:03:42 -040040import android.widget.ImageView;
Joe Onorato86f9bd22010-06-30 17:03:42 -040041
42import com.android.systemui.R;
43
44public class KeyButtonView extends ImageView {
Joe Onoratofd52b182010-11-10 18:00:52 -080045 private static final String TAG = "StatusBar.KeyButtonView";
46
Daniel Sandler96f48182011-08-17 09:50:35 -040047 final float GLOW_MAX_SCALE_FACTOR = 1.8f;
Justin Hobceff712011-10-26 21:01:13 -040048 final float BUTTON_QUIESCENT_ALPHA = 0.6f;
Daniel Sandler96f48182011-08-17 09:50:35 -040049
Joe Onorato86f9bd22010-06-30 17:03:42 -040050 IWindowManager mWindowManager;
51 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);
Daniel Sandleraa051d62011-03-01 16:23:57 -050058
Daniel Sandler804eb852010-08-31 15:43:50 -040059 Runnable mCheckLongPress = new Runnable() {
60 public void run() {
Daniel Sandler804eb852010-08-31 15:43:50 -040061 if (isPressed()) {
Daniel Sandler44a46162011-08-09 16:48:21 -040062 // Slog.d("KeyButtonView", "longpressed: " + this);
Daniel Sandlera375c942011-07-29 00:33:53 -040063 if (mCode != 0) {
Jeff Brown98392ef2011-09-12 18:24:59 -070064 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
Daniel Sandlera375c942011-07-29 00:33:53 -040065 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
66 } else {
67 // Just an old-fashioned ImageView
68 performLongClick();
69 }
Daniel Sandler804eb852010-08-31 15:43:50 -040070 }
71 }
72 };
Joe Onorato86f9bd22010-06-30 17:03:42 -040073
74 public KeyButtonView(Context context, AttributeSet attrs) {
75 this(context, attrs, 0);
76 }
77
78 public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
79 super(context, attrs);
80
81 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
82 defStyle, 0);
83
84 mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
Daniel Sandler44a46162011-08-09 16:48:21 -040085
86 mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
Daniel Sandlera375c942011-07-29 00:33:53 -040087
88 mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
89 if (mGlowBG != null) {
Michael Jurka0b658662012-01-12 05:48:19 -080090 setDrawingAlpha(BUTTON_QUIESCENT_ALPHA);
Joe Onorato86f9bd22010-06-30 17:03:42 -040091 }
92
93 a.recycle();
94
95 mWindowManager = IWindowManager.Stub.asInterface(
96 ServiceManager.getService(Context.WINDOW_SERVICE));
Joe Onorato641bad42011-01-06 15:54:23 -080097
98 setClickable(true);
Daniel Sandleraa051d62011-03-01 16:23:57 -050099 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
Joe Onorato86f9bd22010-06-30 17:03:42 -0400100 }
101
Daniel Sandlera375c942011-07-29 00:33:53 -0400102 @Override
103 protected void onDraw(Canvas canvas) {
104 if (mGlowBG != null) {
105 canvas.save();
106 final int w = getWidth();
107 final int h = getHeight();
108 canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
109 mGlowBG.setBounds(0, 0, w, h);
Michael Jurka0b658662012-01-12 05:48:19 -0800110 mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
Daniel Sandlera375c942011-07-29 00:33:53 -0400111 mGlowBG.draw(canvas);
112 canvas.restore();
Daniel Sandlerc638c1e2011-08-24 16:19:23 -0700113 mRect.right = w;
114 mRect.bottom = h;
Daniel Sandlera375c942011-07-29 00:33:53 -0400115 }
116 super.onDraw(canvas);
Daniel Sandlera375c942011-07-29 00:33:53 -0400117 }
118
119 public float getDrawingAlpha() {
120 if (mGlowBG == null) return 0;
121 return mDrawingAlpha;
122 }
123
124 public void setDrawingAlpha(float x) {
125 if (mGlowBG == null) return;
Michael Jurka0b658662012-01-12 05:48:19 -0800126 // Calling setAlpha(int), which is an ImageView-specific
127 // method that's different from setAlpha(float). This sets
128 // the alpha on this ImageView's drawable directly
129 setAlpha((int) (x * 255));
Daniel Sandlera375c942011-07-29 00:33:53 -0400130 mDrawingAlpha = x;
Daniel Sandlera375c942011-07-29 00:33:53 -0400131 }
132
133 public float getGlowAlpha() {
134 if (mGlowBG == null) return 0;
135 return mGlowAlpha;
136 }
137
138 public void setGlowAlpha(float x) {
139 if (mGlowBG == null) return;
140 mGlowAlpha = x;
141 invalidate();
142 }
143
144 public float getGlowScale() {
145 if (mGlowBG == null) return 0;
146 return mGlowScale;
147 }
148
149 public void setGlowScale(float x) {
150 if (mGlowBG == null) return;
151 mGlowScale = x;
152 final float w = getWidth();
153 final float h = getHeight();
Daniel Sandler96f48182011-08-17 09:50:35 -0400154 if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
155 // this only works if we know the glow will never leave our bounds
Daniel Sandlera375c942011-07-29 00:33:53 -0400156 invalidate();
157 } else {
Daniel Sandler96f48182011-08-17 09:50:35 -0400158 final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
159 final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
Daniel Sandlera375c942011-07-29 00:33:53 -0400160 com.android.systemui.SwipeHelper.invalidateGlobalRegion(
161 this,
Daniel Sandler52985822011-07-29 08:42:15 -0400162 new RectF(getLeft() - rx,
163 getTop() - ry,
164 getRight() + rx,
165 getBottom() + ry));
Daniel Sandler96f48182011-08-17 09:50:35 -0400166
167 // also invalidate our immediate parent to help avoid situations where nearby glows
168 // interfere
169 ((View)getParent()).invalidate();
Daniel Sandlera375c942011-07-29 00:33:53 -0400170 }
171 }
172
173 public void setPressed(boolean pressed) {
174 if (mGlowBG != null) {
175 if (pressed != isPressed()) {
176 AnimatorSet as = new AnimatorSet();
177 if (pressed) {
Justin Hobceff712011-10-26 21:01:13 -0400178 if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
179 mGlowScale = GLOW_MAX_SCALE_FACTOR;
180 if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA)
181 mGlowAlpha = BUTTON_QUIESCENT_ALPHA;
Daniel Sandlera375c942011-07-29 00:33:53 -0400182 setDrawingAlpha(1f);
183 as.playTogether(
184 ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
Daniel Sandler96f48182011-08-17 09:50:35 -0400185 ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR)
Daniel Sandlera375c942011-07-29 00:33:53 -0400186 );
187 as.setDuration(50);
188 } else {
189 as.playTogether(
190 ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
191 ObjectAnimator.ofFloat(this, "glowScale", 1f),
Justin Hobceff712011-10-26 21:01:13 -0400192 ObjectAnimator.ofFloat(this, "drawingAlpha", BUTTON_QUIESCENT_ALPHA)
Daniel Sandlera375c942011-07-29 00:33:53 -0400193 );
194 as.setDuration(500);
195 }
196 as.start();
197 }
198 }
199 super.setPressed(pressed);
200 }
201
Joe Onorato86f9bd22010-06-30 17:03:42 -0400202 public boolean onTouchEvent(MotionEvent ev) {
203 final int action = ev.getAction();
204 int x, y;
205
206 switch (action) {
207 case MotionEvent.ACTION_DOWN:
Christopher Tate7a1a2f02010-09-09 12:22:40 -0700208 //Slog.d("KeyButtonView", "press");
Joe Onorato86f9bd22010-06-30 17:03:42 -0400209 mDownTime = SystemClock.uptimeMillis();
Daniel Sandler44a46162011-08-09 16:48:21 -0400210 setPressed(true);
Jeff Brown98392ef2011-09-12 18:24:59 -0700211 if (mCode != 0) {
212 sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
213 } else {
214 // Provide the same haptic feedback that the system offers for virtual keys.
215 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
216 }
Daniel Sandler44a46162011-08-09 16:48:21 -0400217 if (mSupportsLongpress) {
218 removeCallbacks(mCheckLongPress);
219 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
Daniel Sandler44a46162011-08-09 16:48:21 -0400220 }
Joe Onorato86f9bd22010-06-30 17:03:42 -0400221 break;
222 case MotionEvent.ACTION_MOVE:
Jeff Brown98392ef2011-09-12 18:24:59 -0700223 x = (int)ev.getX();
224 y = (int)ev.getY();
225 setPressed(x >= -mTouchSlop
226 && x < getWidth() + mTouchSlop
227 && y >= -mTouchSlop
228 && y < getHeight() + mTouchSlop);
Joe Onoratoabb27772010-11-15 13:30:12 -0800229 break;
230 case MotionEvent.ACTION_CANCEL:
231 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700232 if (mCode != 0) {
233 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
234 }
235 if (mSupportsLongpress) {
236 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400237 }
238 break;
239 case MotionEvent.ACTION_UP:
Daniel Sandleraa051d62011-03-01 16:23:57 -0500240 final boolean doIt = isPressed();
Daniel Sandler804eb852010-08-31 15:43:50 -0400241 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700242 if (mCode != 0) {
243 if (doIt) {
244 sendEvent(KeyEvent.ACTION_UP, 0);
245 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
246 playSoundEffect(SoundEffectConstants.CLICK);
Patrick Dubroy5ee1a3d2011-03-02 19:11:19 -0800247 } else {
Jeff Brown98392ef2011-09-12 18:24:59 -0700248 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
Daniel Sandleraa051d62011-03-01 16:23:57 -0500249 }
Jeff Brown98392ef2011-09-12 18:24:59 -0700250 } else {
251 // no key code, just a regular ImageView
252 if (doIt) {
253 performClick();
254 }
255 }
256 if (mSupportsLongpress) {
257 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400258 }
259 break;
260 }
261
262 return true;
263 }
264
Jeff Brownbbda99d2010-07-28 15:48:59 -0700265 void sendEvent(int action, int flags) {
266 sendEvent(action, flags, SystemClock.uptimeMillis());
Joe Onorato86f9bd22010-06-30 17:03:42 -0400267 }
268
Jeff Brownbbda99d2010-07-28 15:48:59 -0700269 void sendEvent(int action, int flags, long when) {
Jeff Brown98392ef2011-09-12 18:24:59 -0700270 final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
271 final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
272 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
273 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
274 InputDevice.SOURCE_KEYBOARD);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400275 try {
Joe Onoratofd52b182010-11-10 18:00:52 -0800276 //Slog.d(TAG, "injecting event " + ev);
Jeff Brownbbda99d2010-07-28 15:48:59 -0700277 mWindowManager.injectInputEventNoWait(ev);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400278 } catch (RemoteException ex) {
279 // System process is dead
280 }
281 }
282}
283
284