KeyButtonView.java revision ac14351e16e1258f1cb54e2bf772b8be004eb2b8
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.AnimatorSet; 20import android.animation.ObjectAnimator; 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.graphics.drawable.Drawable; 24import android.graphics.Canvas; 25import android.graphics.RectF; 26import android.hardware.input.InputManager; 27import android.os.RemoteException; 28import android.os.SystemClock; 29import android.os.ServiceManager; 30import android.util.AttributeSet; 31import android.view.accessibility.AccessibilityEvent; 32import android.view.HapticFeedbackConstants; 33import android.view.IWindowManager; 34import android.view.InputDevice; 35import android.view.KeyCharacterMap; 36import android.view.KeyEvent; 37import android.view.MotionEvent; 38import android.view.SoundEffectConstants; 39import android.view.View; 40import android.view.ViewConfiguration; 41import android.widget.ImageView; 42 43import com.android.systemui.R; 44 45public class KeyButtonView extends ImageView { 46 private static final String TAG = "StatusBar.KeyButtonView"; 47 48 final float GLOW_MAX_SCALE_FACTOR = 1.8f; 49 final float BUTTON_QUIESCENT_ALPHA = 0.6f; 50 51 long mDownTime; 52 int mCode; 53 int mTouchSlop; 54 Drawable mGlowBG; 55 float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f; 56 boolean mSupportsLongpress = true; 57 RectF mRect = new RectF(0f,0f,0f,0f); 58 AnimatorSet mPressedAnim; 59 60 Runnable mCheckLongPress = new Runnable() { 61 public void run() { 62 if (isPressed()) { 63 // Slog.d("KeyButtonView", "longpressed: " + this); 64 if (mCode != 0) { 65 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 66 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 67 } else { 68 // Just an old-fashioned ImageView 69 performLongClick(); 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 mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground); 90 if (mGlowBG != null) { 91 setDrawingAlpha(BUTTON_QUIESCENT_ALPHA); 92 } 93 94 a.recycle(); 95 96 setClickable(true); 97 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 98 } 99 100 @Override 101 protected void onDraw(Canvas canvas) { 102 if (mGlowBG != null) { 103 canvas.save(); 104 final int w = getWidth(); 105 final int h = getHeight(); 106 canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f); 107 mGlowBG.setBounds(0, 0, w, h); 108 mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255)); 109 mGlowBG.draw(canvas); 110 canvas.restore(); 111 mRect.right = w; 112 mRect.bottom = h; 113 } 114 super.onDraw(canvas); 115 } 116 117 public float getDrawingAlpha() { 118 if (mGlowBG == null) return 0; 119 return mDrawingAlpha; 120 } 121 122 public void setDrawingAlpha(float x) { 123 if (mGlowBG == null) return; 124 // Calling setAlpha(int), which is an ImageView-specific 125 // method that's different from setAlpha(float). This sets 126 // the alpha on this ImageView's drawable directly 127 setAlpha((int) (x * 255)); 128 mDrawingAlpha = x; 129 } 130 131 public float getGlowAlpha() { 132 if (mGlowBG == null) return 0; 133 return mGlowAlpha; 134 } 135 136 public void setGlowAlpha(float x) { 137 if (mGlowBG == null) return; 138 mGlowAlpha = x; 139 invalidate(); 140 } 141 142 public float getGlowScale() { 143 if (mGlowBG == null) return 0; 144 return mGlowScale; 145 } 146 147 public void setGlowScale(float x) { 148 if (mGlowBG == null) return; 149 mGlowScale = x; 150 final float w = getWidth(); 151 final float h = getHeight(); 152 if (GLOW_MAX_SCALE_FACTOR <= 1.0f) { 153 // this only works if we know the glow will never leave our bounds 154 invalidate(); 155 } else { 156 final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f; 157 final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f; 158 com.android.systemui.SwipeHelper.invalidateGlobalRegion( 159 this, 160 new RectF(getLeft() - rx, 161 getTop() - ry, 162 getRight() + rx, 163 getBottom() + ry)); 164 165 // also invalidate our immediate parent to help avoid situations where nearby glows 166 // interfere 167 ((View)getParent()).invalidate(); 168 } 169 } 170 171 public void setPressed(boolean pressed) { 172 if (mGlowBG != null) { 173 if (pressed != isPressed()) { 174 if (mPressedAnim != null && mPressedAnim.isRunning()) { 175 mPressedAnim.cancel(); 176 } 177 final AnimatorSet as = mPressedAnim = new AnimatorSet(); 178 if (pressed) { 179 if (mGlowScale < GLOW_MAX_SCALE_FACTOR) 180 mGlowScale = GLOW_MAX_SCALE_FACTOR; 181 if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA) 182 mGlowAlpha = BUTTON_QUIESCENT_ALPHA; 183 setDrawingAlpha(1f); 184 as.playTogether( 185 ObjectAnimator.ofFloat(this, "glowAlpha", 1f), 186 ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR) 187 ); 188 as.setDuration(50); 189 } else { 190 as.playTogether( 191 ObjectAnimator.ofFloat(this, "glowAlpha", 0f), 192 ObjectAnimator.ofFloat(this, "glowScale", 1f), 193 ObjectAnimator.ofFloat(this, "drawingAlpha", BUTTON_QUIESCENT_ALPHA) 194 ); 195 as.setDuration(500); 196 } 197 as.start(); 198 } 199 } 200 super.setPressed(pressed); 201 } 202 203 public boolean onTouchEvent(MotionEvent ev) { 204 final int action = ev.getAction(); 205 int x, y; 206 207 switch (action) { 208 case MotionEvent.ACTION_DOWN: 209 //Slog.d("KeyButtonView", "press"); 210 mDownTime = SystemClock.uptimeMillis(); 211 setPressed(true); 212 if (mCode != 0) { 213 sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime); 214 } else { 215 // Provide the same haptic feedback that the system offers for virtual keys. 216 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 217 } 218 if (mSupportsLongpress) { 219 removeCallbacks(mCheckLongPress); 220 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout()); 221 } 222 break; 223 case MotionEvent.ACTION_MOVE: 224 x = (int)ev.getX(); 225 y = (int)ev.getY(); 226 setPressed(x >= -mTouchSlop 227 && x < getWidth() + mTouchSlop 228 && y >= -mTouchSlop 229 && y < getHeight() + mTouchSlop); 230 break; 231 case MotionEvent.ACTION_CANCEL: 232 setPressed(false); 233 if (mCode != 0) { 234 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); 235 } 236 if (mSupportsLongpress) { 237 removeCallbacks(mCheckLongPress); 238 } 239 break; 240 case MotionEvent.ACTION_UP: 241 final boolean doIt = isPressed(); 242 setPressed(false); 243 if (mCode != 0) { 244 if (doIt) { 245 sendEvent(KeyEvent.ACTION_UP, 0); 246 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 247 playSoundEffect(SoundEffectConstants.CLICK); 248 } else { 249 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); 250 } 251 } else { 252 // no key code, just a regular ImageView 253 if (doIt) { 254 performClick(); 255 } 256 } 257 if (mSupportsLongpress) { 258 removeCallbacks(mCheckLongPress); 259 } 260 break; 261 } 262 263 return true; 264 } 265 266 void sendEvent(int action, int flags) { 267 sendEvent(action, flags, SystemClock.uptimeMillis()); 268 } 269 270 void sendEvent(int action, int flags, long when) { 271 final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0; 272 final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount, 273 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 274 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, 275 InputDevice.SOURCE_KEYBOARD); 276 InputManager.injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 277 } 278} 279 280 281