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