KeyButtonView.java revision 5172dc2f36f9c417befd0957ce2ae20f9d083a88
1/* 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 17package com.android.systemui.statusbar.policy; 18 19import android.animation.Animator; 20import android.animation.ObjectAnimator; 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.graphics.drawable.Drawable; 24import android.hardware.input.InputManager; 25import android.os.Bundle; 26import android.os.SystemClock; 27import android.util.AttributeSet; 28import android.util.Log; 29import android.view.HapticFeedbackConstants; 30import android.view.InputDevice; 31import android.view.KeyCharacterMap; 32import android.view.KeyEvent; 33import android.view.MotionEvent; 34import android.view.SoundEffectConstants; 35import android.view.ViewConfiguration; 36import android.view.accessibility.AccessibilityEvent; 37import android.view.accessibility.AccessibilityNodeInfo; 38import android.widget.ImageView; 39 40import com.android.systemui.R; 41 42import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; 43import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; 44 45public class KeyButtonView extends ImageView { 46 private static final String TAG = "StatusBar.KeyButtonView"; 47 private static final boolean DEBUG = false; 48 49 // TODO: Get rid of this 50 public static final float DEFAULT_QUIESCENT_ALPHA = 1f; 51 52 private long mDownTime; 53 private int mCode; 54 private int mTouchSlop; 55 private float mDrawingAlpha = 1f; 56 private float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA; 57 private boolean mSupportsLongpress = true; 58 private Animator mAnimateToQuiescent = new ObjectAnimator(); 59 private Drawable mBackground; 60 61 private final Runnable mCheckLongPress = new Runnable() { 62 public void run() { 63 if (isPressed()) { 64 // Log.d("KeyButtonView", "longpressed: " + this); 65 if (isLongClickable()) { 66 // Just an old-fashioned ImageView 67 performLongClick(); 68 } else { 69 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 70 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 71 } 72 } 73 } 74 }; 75 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); 87 88 mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true); 89 90 Drawable d = getBackground(); 91 if (d != null) { 92 mBackground = d.mutate(); 93 setBackground(mBackground); 94 } 95 96 setDrawingAlpha(mQuiescentAlpha); 97 98 a.recycle(); 99 100 setClickable(true); 101 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 102 } 103 104 @Override 105 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 106 super.onInitializeAccessibilityNodeInfo(info); 107 if (mCode != 0) { 108 info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null)); 109 if (mSupportsLongpress) { 110 info.addAction( 111 new AccessibilityNodeInfo.AccessibilityAction(ACTION_LONG_CLICK, null)); 112 } 113 } 114 } 115 116 @Override 117 public boolean performAccessibilityAction(int action, Bundle arguments) { 118 if (action == ACTION_CLICK && mCode != 0) { 119 sendEvent(KeyEvent.ACTION_DOWN, 0, SystemClock.uptimeMillis()); 120 sendEvent(KeyEvent.ACTION_UP, 0); 121 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 122 playSoundEffect(SoundEffectConstants.CLICK); 123 return true; 124 } else if (action == ACTION_LONG_CLICK && mCode != 0 && mSupportsLongpress) { 125 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 126 sendEvent(KeyEvent.ACTION_UP, 0); 127 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 128 return true; 129 } 130 return super.performAccessibilityAction(action, arguments); 131 } 132 133 public void setQuiescentAlpha(float alpha, boolean animate) { 134 mAnimateToQuiescent.cancel(); 135 alpha = Math.min(Math.max(alpha, 0), 1); 136 if (alpha == mQuiescentAlpha && alpha == mDrawingAlpha) return; 137 mQuiescentAlpha = alpha; 138 if (DEBUG) Log.d(TAG, "New quiescent alpha = " + mQuiescentAlpha); 139 if (mBackground != null && animate) { 140 mAnimateToQuiescent = animateToQuiescent(); 141 mAnimateToQuiescent.start(); 142 } else { 143 setDrawingAlpha(mQuiescentAlpha); 144 } 145 } 146 147 private ObjectAnimator animateToQuiescent() { 148 return ObjectAnimator.ofFloat(this, "drawingAlpha", mQuiescentAlpha); 149 } 150 151 public float getQuiescentAlpha() { 152 return mQuiescentAlpha; 153 } 154 155 public float getDrawingAlpha() { 156 return mDrawingAlpha; 157 } 158 159 public void setDrawingAlpha(float x) { 160 setImageAlpha((int) (x * 255)); 161 if (mBackground != null) { 162 mBackground.setAlpha((int)(x * 255)); 163 } 164 mDrawingAlpha = x; 165 } 166 167 public void setPressed(boolean pressed) { 168 if (mBackground != null) { 169 if (pressed != isPressed()) { 170 if (pressed) { 171 setDrawingAlpha(1f); 172 } else { 173 mAnimateToQuiescent.cancel(); 174 mAnimateToQuiescent = animateToQuiescent(); 175 mAnimateToQuiescent.setDuration(500); 176 mAnimateToQuiescent.start(); 177 } 178 } 179 } 180 super.setPressed(pressed); 181 } 182 183 private void setHotspot(float x, float y) { 184 if (mBackground != null) { 185 mBackground.setHotspot(x, y); 186 } 187 } 188 189 public boolean onTouchEvent(MotionEvent ev) { 190 final int action = ev.getAction(); 191 int x, y; 192 193 switch (action) { 194 case MotionEvent.ACTION_DOWN: 195 //Log.d("KeyButtonView", "press"); 196 mDownTime = SystemClock.uptimeMillis(); 197 setPressed(true); 198 if (mCode != 0) { 199 sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime); 200 } else { 201 // Provide the same haptic feedback that the system offers for virtual keys. 202 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 203 } 204 if (mSupportsLongpress) { 205 removeCallbacks(mCheckLongPress); 206 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout()); 207 } 208 setHotspot(ev.getX(), ev.getY()); 209 break; 210 case MotionEvent.ACTION_MOVE: 211 x = (int)ev.getX(); 212 y = (int)ev.getY(); 213 setPressed(x >= -mTouchSlop 214 && x < getWidth() + mTouchSlop 215 && y >= -mTouchSlop 216 && y < getHeight() + mTouchSlop); 217 setHotspot(ev.getX(), ev.getY()); 218 break; 219 case MotionEvent.ACTION_CANCEL: 220 setPressed(false); 221 if (mCode != 0) { 222 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); 223 } 224 if (mSupportsLongpress) { 225 removeCallbacks(mCheckLongPress); 226 } 227 break; 228 case MotionEvent.ACTION_UP: 229 final boolean doIt = isPressed(); 230 setPressed(false); 231 if (mCode != 0) { 232 if (doIt) { 233 sendEvent(KeyEvent.ACTION_UP, 0); 234 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 235 playSoundEffect(SoundEffectConstants.CLICK); 236 } else { 237 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); 238 } 239 } else { 240 // no key code, just a regular ImageView 241 if (doIt) { 242 performClick(); 243 } 244 } 245 if (mSupportsLongpress) { 246 removeCallbacks(mCheckLongPress); 247 } 248 break; 249 } 250 251 return true; 252 } 253 254 public void sendEvent(int action, int flags) { 255 sendEvent(action, flags, SystemClock.uptimeMillis()); 256 } 257 258 void sendEvent(int action, int flags, long when) { 259 final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0; 260 final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount, 261 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 262 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, 263 InputDevice.SOURCE_KEYBOARD); 264 InputManager.getInstance().injectInputEvent(ev, 265 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 266 } 267} 268 269 270