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