2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.android
.systemui
.statusbar
.policy
;
19 import android
.annotation
.DrawableRes
;
20 import android
.annotation
.Nullable
;
21 import android
.app
.ActivityManager
;
22 import android
.content
.Context
;
23 import android
.content
.res
.Configuration
;
24 import android
.content
.res
.TypedArray
;
25 import android
.graphics
.drawable
.Drawable
;
26 import android
.graphics
.drawable
.Icon
;
27 import android
.hardware
.input
.InputManager
;
28 import android
.media
.AudioManager
;
29 import android
.os
.AsyncTask
;
30 import android
.os
.Bundle
;
31 import android
.os
.SystemClock
;
32 import android
.util
.AttributeSet
;
33 import android
.util
.TypedValue
;
34 import android
.view
.HapticFeedbackConstants
;
35 import android
.view
.InputDevice
;
36 import android
.view
.KeyCharacterMap
;
37 import android
.view
.KeyEvent
;
38 import android
.view
.MotionEvent
;
39 import android
.view
.SoundEffectConstants
;
40 import android
.view
.View
;
41 import android
.view
.ViewConfiguration
;
42 import android
.view
.accessibility
.AccessibilityEvent
;
43 import android
.view
.accessibility
.AccessibilityNodeInfo
;
44 import android
.widget
.ImageView
;
46 import com
.android
.systemui
.R
;
47 import com
.android
.systemui
.statusbar
.phone
.ButtonDispatcher
;
49 import static android
.view
.accessibility
.AccessibilityNodeInfo
.ACTION_CLICK
;
50 import static android
.view
.accessibility
.AccessibilityNodeInfo
.ACTION_LONG_CLICK
;
52 public class KeyButtonView
extends ImageView
implements ButtonDispatcher
.ButtonInterface
{
54 private int mContentDescriptionRes
;
55 private long mDownTime
;
57 private int mTouchSlop
;
58 private boolean mSupportsLongpress
= true;
59 private AudioManager mAudioManager
;
60 private boolean mGestureAborted
;
61 private boolean mLongClicked
;
62 private OnClickListener mOnClickListener
;
64 private final Runnable mCheckLongPress
= new Runnable() {
67 // Log.d("KeyButtonView", "longpressed: " + this);
68 if (isLongClickable()) {
69 // Just an old-fashioned ImageView
72 } else if (mSupportsLongpress
) {
73 sendEvent(KeyEvent
.ACTION_DOWN
, KeyEvent
.FLAG_LONG_PRESS
);
74 sendAccessibilityEvent(AccessibilityEvent
.TYPE_VIEW_LONG_CLICKED
);
81 public KeyButtonView(Context context
, AttributeSet attrs
) {
82 this(context
, attrs
, 0);
85 public KeyButtonView(Context context
, AttributeSet attrs
, int defStyle
) {
86 super(context
, attrs
);
88 TypedArray a
= context
.obtainStyledAttributes(attrs
, R
.styleable
.KeyButtonView
,
91 mCode
= a
.getInteger(R
.styleable
.KeyButtonView_keyCode
, 0);
93 mSupportsLongpress
= a
.getBoolean(R
.styleable
.KeyButtonView_keyRepeat
, true);
95 TypedValue value
= new TypedValue();
96 if (a
.getValue(R
.styleable
.KeyButtonView_android_contentDescription
, value
)) {
97 mContentDescriptionRes
= value
.resourceId
;
104 mTouchSlop
= ViewConfiguration
.get(context
).getScaledTouchSlop();
105 mAudioManager
= (AudioManager
) context
.getSystemService(Context
.AUDIO_SERVICE
);
106 setBackground(new KeyButtonRipple(context
, this));
109 public void setCode(int code
) {
114 public void setOnClickListener(OnClickListener onClickListener
) {
115 super.setOnClickListener(onClickListener
);
116 mOnClickListener
= onClickListener
;
119 public void loadAsync(String uri
) {
120 new AsyncTask
<String
, Void
, Drawable
>() {
122 protected Drawable
doInBackground(String
... params
) {
123 return Icon
.createWithContentUri(params
[0]).loadDrawable(mContext
);
127 protected void onPostExecute(Drawable drawable
) {
128 setImageDrawable(drawable
);
134 protected void onConfigurationChanged(Configuration newConfig
) {
135 super.onConfigurationChanged(newConfig
);
137 if (mContentDescriptionRes
!= 0) {
138 setContentDescription(mContext
.getString(mContentDescriptionRes
));
143 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info
) {
144 super.onInitializeAccessibilityNodeInfo(info
);
146 info
.addAction(new AccessibilityNodeInfo
.AccessibilityAction(ACTION_CLICK
, null));
147 if (mSupportsLongpress
|| isLongClickable()) {
149 new AccessibilityNodeInfo
.AccessibilityAction(ACTION_LONG_CLICK
, null));
155 protected void onWindowVisibilityChanged(int visibility
) {
156 super.onWindowVisibilityChanged(visibility
);
157 if (visibility
!= View
.VISIBLE
) {
158 jumpDrawablesToCurrentState();
163 public boolean performAccessibilityActionInternal(int action
, Bundle arguments
) {
164 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
);
170 } else if (action
== ACTION_LONG_CLICK
&& mCode
!= 0) {
171 sendEvent(KeyEvent
.ACTION_DOWN
, KeyEvent
.FLAG_LONG_PRESS
);
172 sendEvent(KeyEvent
.ACTION_UP
, 0);
173 sendAccessibilityEvent(AccessibilityEvent
.TYPE_VIEW_LONG_CLICKED
);
176 return super.performAccessibilityActionInternal(action
, arguments
);
179 public boolean onTouchEvent(MotionEvent ev
) {
180 final int action
= ev
.getAction();
182 if (action
== MotionEvent
.ACTION_DOWN
) {
183 mGestureAborted
= false;
185 if (mGestureAborted
) {
190 case MotionEvent
.ACTION_DOWN
:
191 mDownTime
= SystemClock
.uptimeMillis();
192 mLongClicked
= false;
195 sendEvent(KeyEvent
.ACTION_DOWN
, 0, mDownTime
);
197 // Provide the same haptic feedback that the system offers for virtual keys.
198 performHapticFeedback(HapticFeedbackConstants
.VIRTUAL_KEY
);
200 playSoundEffect(SoundEffectConstants
.CLICK
);
201 removeCallbacks(mCheckLongPress
);
202 postDelayed(mCheckLongPress
, ViewConfiguration
.getLongPressTimeout());
204 case MotionEvent
.ACTION_MOVE
:
207 setPressed(x
>= -mTouchSlop
208 && x
< getWidth() + mTouchSlop
210 && y
< getHeight() + mTouchSlop
);
212 case MotionEvent
.ACTION_CANCEL
:
215 sendEvent(KeyEvent
.ACTION_UP
, KeyEvent
.FLAG_CANCELED
);
217 removeCallbacks(mCheckLongPress
);
219 case MotionEvent
.ACTION_UP
:
220 final boolean doIt
= isPressed() && !mLongClicked
;
224 sendEvent(KeyEvent
.ACTION_UP
, 0);
225 sendAccessibilityEvent(AccessibilityEvent
.TYPE_VIEW_CLICKED
);
227 sendEvent(KeyEvent
.ACTION_UP
, KeyEvent
.FLAG_CANCELED
);
230 // no key code, just a regular ImageView
231 if (doIt
&& mOnClickListener
!= null) {
232 mOnClickListener
.onClick(this);
233 sendAccessibilityEvent(AccessibilityEvent
.TYPE_VIEW_CLICKED
);
236 removeCallbacks(mCheckLongPress
);
243 public void playSoundEffect(int soundConstant
) {
244 mAudioManager
.playSoundEffect(soundConstant
, ActivityManager
.getCurrentUser());
247 public void sendEvent(int action
, int flags
) {
248 sendEvent(action
, flags
, SystemClock
.uptimeMillis());
251 void sendEvent(int action
, int flags
, long when
) {
252 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
);
257 InputManager
.getInstance().injectInputEvent(ev
,
258 InputManager
.INJECT_INPUT_EVENT_MODE_ASYNC
);
262 public void abortCurrentGesture() {
264 mGestureAborted
= true;
268 public void setImageResource(@DrawableRes int resId
) {
269 super.setImageResource(resId
);
273 public void setImageDrawable(@Nullable Drawable drawable
) {
274 super.setImageDrawable(drawable
);
278 public void setLandscape(boolean landscape
) {
283 public void setCarMode(boolean carMode
) {