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