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