KeyButtonView.java revision 98392efb24d8aac36a4cfa85106812e17d5984f9
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 49 IWindowManager mWindowManager; 50 long mDownTime; 51 int mCode; 52 int mTouchSlop; 53 Drawable mGlowBG; 54 float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f; 55 boolean mSupportsLongpress = true; 56 RectF mRect = new RectF(0f,0f,0f,0f); 57 58 Runnable mCheckLongPress = new Runnable() { 59 public void run() { 60 if (isPressed()) { 61 // Slog.d("KeyButtonView", "longpressed: " + this); 62 if (mCode != 0) { 63 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 64 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 65 } else { 66 // Just an old-fashioned ImageView 67 performLongClick(); 68 } 69 } 70 } 71 }; 72 73 public KeyButtonView(Context context, AttributeSet attrs) { 74 this(context, attrs, 0); 75 } 76 77 public KeyButtonView(Context context, AttributeSet attrs, int defStyle) { 78 super(context, attrs); 79 80 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView, 81 defStyle, 0); 82 83 mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0); 84 85 mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true); 86 87 mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground); 88 if (mGlowBG != null) { 89 mDrawingAlpha = 0.5f; 90 } 91 92 a.recycle(); 93 94 mWindowManager = IWindowManager.Stub.asInterface( 95 ServiceManager.getService(Context.WINDOW_SERVICE)); 96 97 setClickable(true); 98 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 99 } 100 101 @Override 102 protected void onDraw(Canvas canvas) { 103 if (mGlowBG != null) { 104 canvas.save(); 105 final int w = getWidth(); 106 final int h = getHeight(); 107 canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f); 108 mGlowBG.setBounds(0, 0, w, h); 109 mGlowBG.setAlpha((int)(mGlowAlpha * 255)); 110 mGlowBG.draw(canvas); 111 canvas.restore(); 112 mRect.right = w; 113 mRect.bottom = h; 114 canvas.saveLayerAlpha(mRect, (int)(mDrawingAlpha * 255), Canvas.ALL_SAVE_FLAG); 115 } 116 super.onDraw(canvas); 117 if (mGlowBG != null) { 118 canvas.restore(); 119 } 120 } 121 122 public float getDrawingAlpha() { 123 if (mGlowBG == null) return 0; 124 return mDrawingAlpha; 125 } 126 127 public void setDrawingAlpha(float x) { 128 if (mGlowBG == null) return; 129 mDrawingAlpha = x; 130 invalidate(); 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 < 1.7f) mGlowScale = 1.7f; 179 if (mGlowAlpha < 0.5f) mGlowAlpha = 0.5f; 180 setDrawingAlpha(1f); 181 as.playTogether( 182 ObjectAnimator.ofFloat(this, "glowAlpha", 1f), 183 ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR) 184 ); 185 as.setDuration(50); 186 } else { 187 as.playTogether( 188 ObjectAnimator.ofFloat(this, "glowAlpha", 0f), 189 ObjectAnimator.ofFloat(this, "glowScale", 1f), 190 ObjectAnimator.ofFloat(this, "drawingAlpha", 0.5f) 191 ); 192 as.setDuration(500); 193 } 194 as.start(); 195 } 196 } 197 super.setPressed(pressed); 198 } 199 200 public boolean onTouchEvent(MotionEvent ev) { 201 final int action = ev.getAction(); 202 int x, y; 203 204 switch (action) { 205 case MotionEvent.ACTION_DOWN: 206 //Slog.d("KeyButtonView", "press"); 207 mDownTime = SystemClock.uptimeMillis(); 208 setPressed(true); 209 if (mCode != 0) { 210 sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime); 211 } else { 212 // Provide the same haptic feedback that the system offers for virtual keys. 213 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 214 } 215 if (mSupportsLongpress) { 216 removeCallbacks(mCheckLongPress); 217 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout()); 218 } 219 break; 220 case MotionEvent.ACTION_MOVE: 221 x = (int)ev.getX(); 222 y = (int)ev.getY(); 223 setPressed(x >= -mTouchSlop 224 && x < getWidth() + mTouchSlop 225 && y >= -mTouchSlop 226 && y < getHeight() + mTouchSlop); 227 break; 228 case MotionEvent.ACTION_CANCEL: 229 setPressed(false); 230 if (mCode != 0) { 231 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); 232 } 233 if (mSupportsLongpress) { 234 removeCallbacks(mCheckLongPress); 235 } 236 break; 237 case MotionEvent.ACTION_UP: 238 final boolean doIt = isPressed(); 239 setPressed(false); 240 if (mCode != 0) { 241 if (doIt) { 242 sendEvent(KeyEvent.ACTION_UP, 0); 243 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 244 playSoundEffect(SoundEffectConstants.CLICK); 245 } else { 246 sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); 247 } 248 } else { 249 // no key code, just a regular ImageView 250 if (doIt) { 251 performClick(); 252 } 253 } 254 if (mSupportsLongpress) { 255 removeCallbacks(mCheckLongPress); 256 } 257 break; 258 } 259 260 return true; 261 } 262 263 void sendEvent(int action, int flags) { 264 sendEvent(action, flags, SystemClock.uptimeMillis()); 265 } 266 267 void sendEvent(int action, int flags, long when) { 268 final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0; 269 final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount, 270 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 271 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, 272 InputDevice.SOURCE_KEYBOARD); 273 try { 274 //Slog.d(TAG, "injecting event " + ev); 275 mWindowManager.injectInputEventNoWait(ev); 276 } catch (RemoteException ex) { 277 // System process is dead 278 } 279 } 280} 281 282 283