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