KeyguardWidgetFrame.java revision 8a7785c7aa2df74203276299e10b0d9056cd0560
1/* 2 * Copyright (C) 2012 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.internal.policy.impl.keyguard; 18 19import android.animation.Animator; 20import android.animation.ObjectAnimator; 21import android.animation.PropertyValuesHolder; 22import android.appwidget.AppWidgetHostView; 23import android.content.Context; 24import android.content.res.Resources; 25import android.graphics.Canvas; 26import android.graphics.LinearGradient; 27import android.graphics.Paint; 28import android.graphics.PorterDuff; 29import android.graphics.PorterDuffXfermode; 30import android.graphics.Rect; 31import android.graphics.Shader; 32import android.graphics.drawable.Drawable; 33import android.util.AttributeSet; 34import android.view.MotionEvent; 35import android.view.View; 36import android.widget.FrameLayout; 37 38import com.android.internal.R; 39 40public class KeyguardWidgetFrame extends FrameLayout { 41 private final static PorterDuffXfermode sAddBlendMode = 42 new PorterDuffXfermode(PorterDuff.Mode.ADD); 43 44 static final float OUTLINE_ALPHA_MULTIPLIER = 0.6f; 45 46 private int mGradientColor; 47 private LinearGradient mForegroundGradient; 48 private LinearGradient mLeftToRightGradient; 49 private LinearGradient mRightToLeftGradient; 50 private Paint mGradientPaint = new Paint(); 51 boolean mLeftToRight = true; 52 53 private float mOverScrollAmount = 0f; 54 private final Rect mForegroundRect = new Rect(); 55 private int mForegroundAlpha = 0; 56 private CheckLongPressHelper mLongPressHelper; 57 private Animator mFrameFade; 58 private boolean mIsSmall = false; 59 60 private float mBackgroundAlpha; 61 private float mContentAlpha; 62 private float mBackgroundAlphaMultiplier = 1.0f; 63 private Drawable mBackgroundDrawable; 64 private Rect mBackgroundRect = new Rect(); 65 private static int mSmallWidgetHeight; 66 67 // Multiple callers may try and adjust the alpha of the frame. When a caller shows 68 // the outlines, we give that caller control, and nobody else can fade them out. 69 // This prevents animation conflicts. 70 private Object mBgAlphaController; 71 72 public KeyguardWidgetFrame(Context context) { 73 this(context, null, 0); 74 } 75 76 public KeyguardWidgetFrame(Context context, AttributeSet attrs) { 77 this(context, attrs, 0); 78 } 79 80 public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) { 81 super(context, attrs, defStyle); 82 83 mLongPressHelper = new CheckLongPressHelper(this); 84 85 Resources res = context.getResources(); 86 // TODO: this padding should really correspond to the padding embedded in the background 87 // drawable (ie. outlines). 88 int padding = (int) (res.getDisplayMetrics().density * 8); 89 setPadding(padding, padding, padding, padding); 90 91 mBackgroundDrawable = res.getDrawable(R.drawable.kg_bouncer_bg_white); 92 mGradientColor = res.getColor(com.android.internal.R.color.kg_widget_pager_gradient); 93 mGradientPaint.setXfermode(sAddBlendMode); 94 } 95 96 @Override 97 protected void onDetachedFromWindow() { 98 cancelLongPress(); 99 } 100 101 public void setMaxChallengeTop(int top) { 102 mSmallWidgetHeight = top - getPaddingTop(); 103 } 104 105 @Override 106 public boolean onInterceptTouchEvent(MotionEvent ev) { 107 // Watch for longpress events at this level to make sure 108 // users can always pick up this widget 109 switch (ev.getAction()) { 110 case MotionEvent.ACTION_DOWN: 111 mLongPressHelper.postCheckForLongPress(ev); 112 break; 113 case MotionEvent.ACTION_MOVE: 114 mLongPressHelper.onMove(ev); 115 break; 116 case MotionEvent.ACTION_POINTER_DOWN: 117 case MotionEvent.ACTION_UP: 118 case MotionEvent.ACTION_CANCEL: 119 mLongPressHelper.cancelLongPress(); 120 break; 121 } 122 123 // Otherwise continue letting touch events fall through to children 124 return false; 125 } 126 127 @Override 128 public boolean onTouchEvent(MotionEvent ev) { 129 // Watch for longpress events at this level to make sure 130 // users can always pick up this widget 131 switch (ev.getAction()) { 132 case MotionEvent.ACTION_MOVE: 133 mLongPressHelper.onMove(ev); 134 break; 135 case MotionEvent.ACTION_POINTER_DOWN: 136 case MotionEvent.ACTION_UP: 137 case MotionEvent.ACTION_CANCEL: 138 mLongPressHelper.cancelLongPress(); 139 break; 140 } 141 142 // We return true here to ensure that we will get cancel / up signal 143 // even if none of our children have requested touch. 144 return true; 145 } 146 147 @Override 148 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 149 super.requestDisallowInterceptTouchEvent(disallowIntercept); 150 cancelLongPress(); 151 } 152 153 @Override 154 public void cancelLongPress() { 155 super.cancelLongPress(); 156 mLongPressHelper.cancelLongPress(); 157 } 158 159 160 private void drawGradientOverlay(Canvas c) { 161 mGradientPaint.setShader(mForegroundGradient); 162 mGradientPaint.setAlpha(mForegroundAlpha); 163 c.drawRect(mForegroundRect, mGradientPaint); 164 } 165 166 protected void drawBg(Canvas canvas) { 167 if (mBackgroundAlpha > 0.0f) { 168 Drawable bg = mBackgroundDrawable; 169 170 bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255)); 171 bg.setBounds(mBackgroundRect); 172 bg.draw(canvas); 173 } 174 } 175 176 @Override 177 protected void dispatchDraw(Canvas canvas) { 178 drawBg(canvas); 179 super.dispatchDraw(canvas); 180 drawGradientOverlay(canvas); 181 } 182 183 /** 184 * Because this view has fading outlines, it is essential that we enable hardware 185 * layers on the content (child) so that updating the alpha of the outlines doesn't 186 * result in the content layer being recreated. 187 */ 188 public void enableHardwareLayersForContent() { 189 View widget = getContent(); 190 if (widget != null) { 191 widget.setLayerType(LAYER_TYPE_HARDWARE, null); 192 } 193 } 194 195 /** 196 * Because this view has fading outlines, it is essential that we enable hardware 197 * layers on the content (child) so that updating the alpha of the outlines doesn't 198 * result in the content layer being recreated. 199 */ 200 public void disableHardwareLayersForContent() { 201 View widget = getContent(); 202 if (widget != null) { 203 widget.setLayerType(LAYER_TYPE_NONE, null); 204 } 205 } 206 207 public void enableHardwareLayers() { 208 setLayerType(LAYER_TYPE_HARDWARE, null); 209 } 210 211 public void disableHardwareLayers() { 212 setLayerType(LAYER_TYPE_NONE, null); 213 } 214 215 public View getContent() { 216 return getChildAt(0); 217 } 218 219 public int getContentAppWidgetId() { 220 View content = getContent(); 221 if (content instanceof AppWidgetHostView) { 222 return ((AppWidgetHostView) content).getAppWidgetId(); 223 } else { 224 return ((KeyguardStatusView) content).getAppWidgetId(); 225 } 226 } 227 228 public float getBackgroundAlpha() { 229 return mBackgroundAlpha; 230 } 231 232 public void setBackgroundAlphaMultiplier(float multiplier) { 233 if (Float.compare(mBackgroundAlphaMultiplier, multiplier) != 0) { 234 mBackgroundAlphaMultiplier = multiplier; 235 invalidate(); 236 } 237 } 238 239 public float getBackgroundAlphaMultiplier() { 240 return mBackgroundAlphaMultiplier; 241 } 242 243 public void setBackgroundAlpha(float alpha) { 244 if (Float.compare(mBackgroundAlpha, alpha) != 0) { 245 mBackgroundAlpha = alpha; 246 invalidate(); 247 } 248 } 249 250 public float getContentAlpha() { 251 return mContentAlpha; 252 } 253 254 public void setContentAlpha(float alpha) { 255 mContentAlpha = alpha; 256 View content = getContent(); 257 if (content != null) { 258 content.setAlpha(alpha); 259 } 260 } 261 262 /** 263 * Set the top location of the challenge. 264 * 265 * @param top The top of the challenge, in _local_ coordinates, or -1 to indicate the challenge 266 * is down. 267 */ 268 private void setChallengeTop(int top, boolean updateWidgetSize) { 269 // The widget starts below the padding, and extends to the top of the challengs. 270 int widgetHeight = top - getPaddingTop(); 271 int frameHeight = top + getPaddingBottom(); 272 setFrameHeight(frameHeight); 273 if (updateWidgetSize) { 274 setWidgetHeight(widgetHeight); 275 } 276 } 277 278 /** 279 * Depending on whether the security is up, the widget size needs to change 280 * 281 * @param height The height of the widget, -1 for full height 282 */ 283 private void setWidgetHeight(int height) { 284 boolean needLayout = false; 285 View widget = getContent(); 286 if (widget != null) { 287 LayoutParams lp = (LayoutParams) widget.getLayoutParams(); 288 if (lp.height != height) { 289 needLayout = true; 290 lp.height = height; 291 } 292 } 293 if (needLayout) { 294 requestLayout(); 295 } 296 } 297 298 public boolean isSmall() { 299 return mIsSmall; 300 } 301 302 public void adjustFrame(int challengeTop) { 303 setChallengeTop(challengeTop, false); 304 } 305 306 public void shrinkWidget() { 307 mIsSmall = true; 308 setChallengeTop(mSmallWidgetHeight, true); 309 } 310 311 public void resetSize() { 312 mIsSmall = false; 313 setFrameHeight(getMeasuredHeight()); 314 setWidgetHeight(LayoutParams.MATCH_PARENT); 315 } 316 317 public void setFrameHeight(int height) { 318 height = Math.min(height, getMeasuredHeight()); 319 mBackgroundRect.set(0, 0, getMeasuredWidth(), height); 320 invalidate(); 321 } 322 323 public void hideFrame(Object caller) { 324 fadeFrame(caller, false, 0f, 150); 325 } 326 327 public void showFrame(Object caller) { 328 fadeFrame(caller, true, OUTLINE_ALPHA_MULTIPLIER, 150); 329 } 330 331 public void fadeFrame(Object caller, boolean takeControl, float alpha, int duration) { 332 if (takeControl) { 333 mBgAlphaController = caller; 334 } 335 336 if (mBgAlphaController != caller) return; 337 338 if (mFrameFade != null) { 339 mFrameFade.cancel(); 340 mFrameFade = null; 341 } 342 PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", alpha); 343 mFrameFade = ObjectAnimator.ofPropertyValuesHolder(this, bgAlpha); 344 mFrameFade.setDuration(duration); 345 mFrameFade.start(); 346 } 347 348 @Override 349 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 350 super.onSizeChanged(w, h, oldw, oldh); 351 mForegroundRect.set(getPaddingLeft(), getPaddingTop(), 352 w - getPaddingRight(), h - getPaddingBottom()); 353 float x0 = mLeftToRight ? 0 : mForegroundRect.width(); 354 float x1 = mLeftToRight ? mForegroundRect.width(): 0; 355 mLeftToRightGradient = new LinearGradient(x0, 0f, x1, 0f, 356 mGradientColor, 0, Shader.TileMode.CLAMP); 357 mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f, 358 mGradientColor, 0, Shader.TileMode.CLAMP); 359 mBackgroundRect.set(0, 0, w, h); 360 invalidate(); 361 } 362 363 void setOverScrollAmount(float r, boolean left) { 364 if (Float.compare(mOverScrollAmount, r) != 0) { 365 mOverScrollAmount = r; 366 mForegroundGradient = left ? mLeftToRightGradient : mRightToLeftGradient; 367 mForegroundAlpha = (int) Math.round((0.85f * r * 255)); 368 invalidate(); 369 } 370 } 371 372 public void onActive(boolean isActive) { 373 // hook for subclasses 374 } 375 376 public boolean onUserInteraction(int action) { 377 // hook for subclasses 378 return false; 379 } 380 381 public void onChallengeActive(boolean challengeActive) { 382 // hook for subclasses 383 } 384} 385