blob: ae593158c2668915d4eafb6727516473856f7406 [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
Annie Chin1ea49352016-05-27 15:23:35 -070019import android.annotation.DrawableRes;
20import android.annotation.Nullable;
Jason Monk0c37ba32014-09-08 15:34:23 -040021import android.app.ActivityManager;
Joe Onorato86f9bd22010-06-30 17:03:42 -040022import android.content.Context;
Adrian Roos3bab5152015-10-16 16:51:04 -070023import android.content.res.Configuration;
Joe Onorato86f9bd22010-06-30 17:03:42 -040024import android.content.res.TypedArray;
Jason Monk8457ad82016-01-24 10:15:55 -050025import android.graphics.drawable.Drawable;
26import android.graphics.drawable.Icon;
Jeff Brownac143512012-04-05 18:57:33 -070027import android.hardware.input.InputManager;
Jason Monk0c37ba32014-09-08 15:34:23 -040028import android.media.AudioManager;
Jason Monk8457ad82016-01-24 10:15:55 -050029import android.os.AsyncTask;
Jorim Jaggi7beadfc2014-08-15 18:42:00 +020030import android.os.Bundle;
Joe Onorato86f9bd22010-06-30 17:03:42 -040031import android.os.SystemClock;
Joe Onorato86f9bd22010-06-30 17:03:42 -040032import android.util.AttributeSet;
Adrian Roos3bab5152015-10-16 16:51:04 -070033import android.util.TypedValue;
Daniel Sandler804eb852010-08-31 15:43:50 -040034import android.view.HapticFeedbackConstants;
Jeff Brownbbda99d2010-07-28 15:48:59 -070035import android.view.InputDevice;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080036import android.view.KeyCharacterMap;
Joe Onorato86f9bd22010-06-30 17:03:42 -040037import android.view.KeyEvent;
38import android.view.MotionEvent;
Daniel Sandleraa051d62011-03-01 16:23:57 -050039import android.view.SoundEffectConstants;
Christian Robertson42310962014-09-14 02:14:46 -040040import android.view.View;
Daniel Sandler804eb852010-08-31 15:43:50 -040041import android.view.ViewConfiguration;
John Spurlockde84f0e2013-06-12 12:41:00 -040042import android.view.accessibility.AccessibilityEvent;
Jorim Jaggi7beadfc2014-08-15 18:42:00 +020043import android.view.accessibility.AccessibilityNodeInfo;
Joe Onorato86f9bd22010-06-30 17:03:42 -040044import android.widget.ImageView;
Joe Onorato86f9bd22010-06-30 17:03:42 -040045
46import com.android.systemui.R;
Jason Monk197f4db2016-08-26 13:28:20 -040047import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
Joe Onorato86f9bd22010-06-30 17:03:42 -040048
Jorim Jaggi7beadfc2014-08-15 18:42:00 +020049import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
50import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
51
Jason Monk197f4db2016-08-26 13:28:20 -040052public class KeyButtonView extends ImageView implements ButtonInterface {
Daniel Sandler96f48182011-08-17 09:50:35 -040053
Adrian Roos3bab5152015-10-16 16:51:04 -070054 private int mContentDescriptionRes;
Adrian Rooscde52d72014-05-26 22:21:46 +020055 private long mDownTime;
56 private int mCode;
57 private int mTouchSlop;
Adrian Rooscde52d72014-05-26 22:21:46 +020058 private boolean mSupportsLongpress = true;
Jason Monk0c37ba32014-09-08 15:34:23 -040059 private AudioManager mAudioManager;
Jorim Jaggi2fdeeab2015-04-01 15:13:03 -070060 private boolean mGestureAborted;
Jorim Jaggi053f2182015-11-24 16:07:55 -080061 private boolean mLongClicked;
Jorim Jaggi05bce152016-08-25 18:14:14 -070062 private OnClickListener mOnClickListener;
Daniel Sandleraa051d62011-03-01 16:23:57 -050063
Adrian Rooscde52d72014-05-26 22:21:46 +020064 private final Runnable mCheckLongPress = new Runnable() {
Daniel Sandler804eb852010-08-31 15:43:50 -040065 public void run() {
Daniel Sandler804eb852010-08-31 15:43:50 -040066 if (isPressed()) {
John Spurlockcd686b52013-06-05 10:13:46 -040067 // Log.d("KeyButtonView", "longpressed: " + this);
Jason Monk815e0572014-08-12 17:26:36 -040068 if (isLongClickable()) {
Daniel Sandlera375c942011-07-29 00:33:53 -040069 // Just an old-fashioned ImageView
70 performLongClick();
Jorim Jaggi053f2182015-11-24 16:07:55 -080071 mLongClicked = true;
Jorim Jaggi7e6571f2015-06-25 11:52:48 -070072 } else if (mSupportsLongpress) {
Jason Monk815e0572014-08-12 17:26:36 -040073 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
74 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
Jorim Jaggi053f2182015-11-24 16:07:55 -080075 mLongClicked = true;
Daniel Sandlera375c942011-07-29 00:33:53 -040076 }
Daniel Sandler804eb852010-08-31 15:43:50 -040077 }
78 }
79 };
Joe Onorato86f9bd22010-06-30 17:03:42 -040080
81 public KeyButtonView(Context context, AttributeSet attrs) {
82 this(context, attrs, 0);
83 }
84
85 public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
86 super(context, attrs);
87
88 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
89 defStyle, 0);
90
91 mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
John Spurlock209bede2013-07-17 12:23:27 -040092
Daniel Sandler44a46162011-08-09 16:48:21 -040093 mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
Daniel Sandlera375c942011-07-29 00:33:53 -040094
Adrian Roos3bab5152015-10-16 16:51:04 -070095 TypedValue value = new TypedValue();
96 if (a.getValue(R.styleable.KeyButtonView_android_contentDescription, value)) {
97 mContentDescriptionRes = value.resourceId;
98 }
99
Joe Onorato86f9bd22010-06-30 17:03:42 -0400100 a.recycle();
101
Adrian Roos3bab5152015-10-16 16:51:04 -0700102
Joe Onorato641bad42011-01-06 15:54:23 -0800103 setClickable(true);
Daniel Sandleraa051d62011-03-01 16:23:57 -0500104 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
Jason Monk0c37ba32014-09-08 15:34:23 -0400105 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Jorim Jaggi072707d2014-09-15 17:20:08 +0200106 setBackground(new KeyButtonRipple(context, this));
Joe Onorato86f9bd22010-06-30 17:03:42 -0400107 }
108
Jason Monk8457ad82016-01-24 10:15:55 -0500109 public void setCode(int code) {
110 mCode = code;
111 }
112
Jorim Jaggi05bce152016-08-25 18:14:14 -0700113 @Override
114 public void setOnClickListener(OnClickListener onClickListener) {
115 super.setOnClickListener(onClickListener);
116 mOnClickListener = onClickListener;
117 }
118
Jason Monk8457ad82016-01-24 10:15:55 -0500119 public void loadAsync(String uri) {
120 new AsyncTask<String, Void, Drawable>() {
121 @Override
122 protected Drawable doInBackground(String... params) {
123 return Icon.createWithContentUri(params[0]).loadDrawable(mContext);
124 }
125
126 @Override
127 protected void onPostExecute(Drawable drawable) {
128 setImageDrawable(drawable);
129 }
130 }.execute(uri);
131 }
132
Jorim Jaggi7beadfc2014-08-15 18:42:00 +0200133 @Override
Adrian Roos3bab5152015-10-16 16:51:04 -0700134 protected void onConfigurationChanged(Configuration newConfig) {
135 super.onConfigurationChanged(newConfig);
136
137 if (mContentDescriptionRes != 0) {
138 setContentDescription(mContext.getString(mContentDescriptionRes));
139 }
140 }
141
142 @Override
Jorim Jaggi7beadfc2014-08-15 18:42:00 +0200143 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
144 super.onInitializeAccessibilityNodeInfo(info);
145 if (mCode != 0) {
146 info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null));
Jorim Jaggi7e6571f2015-06-25 11:52:48 -0700147 if (mSupportsLongpress || isLongClickable()) {
Jorim Jaggi7beadfc2014-08-15 18:42:00 +0200148 info.addAction(
149 new AccessibilityNodeInfo.AccessibilityAction(ACTION_LONG_CLICK, null));
150 }
151 }
152 }
153
154 @Override
Jorim Jaggib9e290c2014-10-28 19:04:20 +0100155 protected void onWindowVisibilityChanged(int visibility) {
156 super.onWindowVisibilityChanged(visibility);
157 if (visibility != View.VISIBLE) {
158 jumpDrawablesToCurrentState();
159 }
160 }
161
162 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800163 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
Jorim Jaggi7beadfc2014-08-15 18:42:00 +0200164 if (action == ACTION_CLICK && mCode != 0) {
165 sendEvent(KeyEvent.ACTION_DOWN, 0, SystemClock.uptimeMillis());
166 sendEvent(KeyEvent.ACTION_UP, 0);
167 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
168 playSoundEffect(SoundEffectConstants.CLICK);
169 return true;
Jorim Jaggi7e6571f2015-06-25 11:52:48 -0700170 } else if (action == ACTION_LONG_CLICK && mCode != 0) {
Jorim Jaggi7beadfc2014-08-15 18:42:00 +0200171 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
172 sendEvent(KeyEvent.ACTION_UP, 0);
173 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
174 return true;
175 }
Alan Viverettea54956a2015-01-07 16:05:02 -0800176 return super.performAccessibilityActionInternal(action, arguments);
Jorim Jaggi7beadfc2014-08-15 18:42:00 +0200177 }
178
Joe Onorato86f9bd22010-06-30 17:03:42 -0400179 public boolean onTouchEvent(MotionEvent ev) {
180 final int action = ev.getAction();
181 int x, y;
Jorim Jaggi2fdeeab2015-04-01 15:13:03 -0700182 if (action == MotionEvent.ACTION_DOWN) {
183 mGestureAborted = false;
184 }
185 if (mGestureAborted) {
186 return false;
187 }
Joe Onorato86f9bd22010-06-30 17:03:42 -0400188
189 switch (action) {
190 case MotionEvent.ACTION_DOWN:
191 mDownTime = SystemClock.uptimeMillis();
Jorim Jaggi053f2182015-11-24 16:07:55 -0800192 mLongClicked = false;
Daniel Sandler44a46162011-08-09 16:48:21 -0400193 setPressed(true);
Jeff Brown98392ef2011-09-12 18:24:59 -0700194 if (mCode != 0) {
195 sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
196 } else {
197 // Provide the same haptic feedback that the system offers for virtual keys.
198 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
199 }
Jorim Jaggi05bce152016-08-25 18:14:14 -0700200 playSoundEffect(SoundEffectConstants.CLICK);
Jorim Jaggi7e6571f2015-06-25 11:52:48 -0700201 removeCallbacks(mCheckLongPress);
202 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
Joe Onorato86f9bd22010-06-30 17:03:42 -0400203 break;
204 case MotionEvent.ACTION_MOVE:
Jeff Brown98392ef2011-09-12 18:24:59 -0700205 x = (int)ev.getX();
206 y = (int)ev.getY();
207 setPressed(x >= -mTouchSlop
208 && x < getWidth() + mTouchSlop
209 && y >= -mTouchSlop
210 && y < getHeight() + mTouchSlop);
Joe Onoratoabb27772010-11-15 13:30:12 -0800211 break;
212 case MotionEvent.ACTION_CANCEL:
213 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700214 if (mCode != 0) {
215 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
216 }
Jorim Jaggi7e6571f2015-06-25 11:52:48 -0700217 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400218 break;
219 case MotionEvent.ACTION_UP:
Jorim Jaggi053f2182015-11-24 16:07:55 -0800220 final boolean doIt = isPressed() && !mLongClicked;
Daniel Sandler804eb852010-08-31 15:43:50 -0400221 setPressed(false);
Jeff Brown98392ef2011-09-12 18:24:59 -0700222 if (mCode != 0) {
223 if (doIt) {
224 sendEvent(KeyEvent.ACTION_UP, 0);
225 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
Patrick Dubroy5ee1a3d2011-03-02 19:11:19 -0800226 } else {
Jeff Brown98392ef2011-09-12 18:24:59 -0700227 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
Daniel Sandleraa051d62011-03-01 16:23:57 -0500228 }
Jeff Brown98392ef2011-09-12 18:24:59 -0700229 } else {
230 // no key code, just a regular ImageView
Jorim Jaggi05bce152016-08-25 18:14:14 -0700231 if (doIt && mOnClickListener != null) {
232 mOnClickListener.onClick(this);
233 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
Jeff Brown98392ef2011-09-12 18:24:59 -0700234 }
235 }
Jorim Jaggi7e6571f2015-06-25 11:52:48 -0700236 removeCallbacks(mCheckLongPress);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400237 break;
238 }
239
240 return true;
241 }
242
Jason Monk0c37ba32014-09-08 15:34:23 -0400243 public void playSoundEffect(int soundConstant) {
244 mAudioManager.playSoundEffect(soundConstant, ActivityManager.getCurrentUser());
245 };
246
Jason Monk815e0572014-08-12 17:26:36 -0400247 public void sendEvent(int action, int flags) {
Jeff Brownbbda99d2010-07-28 15:48:59 -0700248 sendEvent(action, flags, SystemClock.uptimeMillis());
Joe Onorato86f9bd22010-06-30 17:03:42 -0400249 }
250
Jeff Brownbbda99d2010-07-28 15:48:59 -0700251 void sendEvent(int action, int flags, long when) {
Jeff Brown98392ef2011-09-12 18:24:59 -0700252 final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
253 final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
254 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
255 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
256 InputDevice.SOURCE_KEYBOARD);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700257 InputManager.getInstance().injectInputEvent(ev,
258 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
Joe Onorato86f9bd22010-06-30 17:03:42 -0400259 }
Jorim Jaggi2fdeeab2015-04-01 15:13:03 -0700260
Annie Chin1ea49352016-05-27 15:23:35 -0700261 @Override
Jorim Jaggi2fdeeab2015-04-01 15:13:03 -0700262 public void abortCurrentGesture() {
263 setPressed(false);
264 mGestureAborted = true;
265 }
Annie Chin1ea49352016-05-27 15:23:35 -0700266
267 @Override
268 public void setImageResource(@DrawableRes int resId) {
269 super.setImageResource(resId);
270 }
271
272 @Override
273 public void setImageDrawable(@Nullable Drawable drawable) {
274 super.setImageDrawable(drawable);
275 }
Annie Chin6fc46002016-07-07 18:38:12 -0700276
277 @Override
Xiaohui Chen40e978e2016-11-29 15:10:04 -0800278 public void setVertical(boolean vertical) {
Annie Chin6fc46002016-07-07 18:38:12 -0700279 //no op
280 }
Annie Chin389f0912016-07-29 15:09:55 -0700281
282 @Override
283 public void setCarMode(boolean carMode) {
284 // no op
285 }
Joe Onorato86f9bd22010-06-30 17:03:42 -0400286}
287
288