1 /* 2 * Copyright (C) 2014 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 17 package com.android.systemui.statusbar.phone; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.PropertyValuesHolder; 22 import android.animation.ValueAnimator; 23 import android.app.WallpaperManager; 24 import android.content.Context; 25 import android.graphics.Color; 26 import android.graphics.Rect; 27 import android.graphics.drawable.Drawable; 28 import android.os.Trace; 29 import android.util.MathUtils; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.view.ViewTreeObserver; 33 import android.view.animation.DecelerateInterpolator; 34 import android.view.animation.Interpolator; 35 import android.view.animation.PathInterpolator; 36 37 import com.android.internal.colorextraction.ColorExtractor; 38 import com.android.internal.colorextraction.ColorExtractor.GradientColors; 39 import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener; 40 import com.android.internal.graphics.ColorUtils; 41 import com.android.keyguard.KeyguardUpdateMonitor; 42 import com.android.systemui.Dependency; 43 import com.android.systemui.R; 44 import com.android.systemui.colorextraction.SysuiColorExtractor; 45 import com.android.systemui.statusbar.ExpandableNotificationRow; 46 import com.android.systemui.statusbar.NotificationData; 47 import com.android.systemui.statusbar.ScrimView; 48 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 49 import com.android.systemui.statusbar.stack.ViewState; 50 51 import java.io.PrintWriter; 52 import java.util.function.Consumer; 53 54 /** 55 * Controls both the scrim behind the notifications and in front of the notifications (when a 56 * security method gets shown). 57 */ 58 public class ScrimController implements ViewTreeObserver.OnPreDrawListener, 59 OnHeadsUpChangedListener, OnColorsChangedListener { 60 public static final long ANIMATION_DURATION = 220; 61 public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR 62 = new PathInterpolator(0f, 0, 0.7f, 1f); 63 public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED 64 = new PathInterpolator(0.3f, 0f, 0.8f, 1f); 65 // Default alpha value for most scrims, if unsure use this constant 66 public static final float GRADIENT_SCRIM_ALPHA = 0.45f; 67 // A scrim varies its opacity based on a busyness factor, for example 68 // how many notifications are currently visible. 69 public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f; 70 protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA; 71 protected static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; 72 private static final float SCRIM_IN_FRONT_ALPHA = GRADIENT_SCRIM_ALPHA_BUSY; 73 private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY; 74 private static final int TAG_KEY_ANIM = R.id.scrim; 75 private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target; 76 private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; 77 private static final int TAG_END_ALPHA = R.id.scrim_alpha_end; 78 private static final float NOT_INITIALIZED = -1; 79 80 private final LightBarController mLightBarController; 81 protected final ScrimView mScrimBehind; 82 protected final ScrimView mScrimInFront; 83 private final UnlockMethodCache mUnlockMethodCache; 84 private final View mHeadsUpScrim; 85 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 86 87 private final SysuiColorExtractor mColorExtractor; 88 private GradientColors mLockColors; 89 private GradientColors mSystemColors; 90 private boolean mNeedsDrawableColorUpdate; 91 92 protected float mScrimBehindAlpha; 93 protected float mScrimBehindAlphaResValue; 94 protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; 95 protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING; 96 97 protected boolean mKeyguardShowing; 98 private float mFraction; 99 100 private boolean mDarkenWhileDragging; 101 protected boolean mBouncerShowing; 102 protected boolean mBouncerIsKeyguard = false; 103 private boolean mWakeAndUnlocking; 104 protected boolean mAnimateChange; 105 private boolean mUpdatePending; 106 private boolean mTracking; 107 private boolean mAnimateKeyguardFadingOut; 108 protected long mDurationOverride = -1; 109 private long mAnimationDelay; 110 private Runnable mOnAnimationFinished; 111 private boolean mDeferFinishedListener; 112 private final Interpolator mInterpolator = new DecelerateInterpolator(); 113 private boolean mDozing; 114 private float mDozeInFrontAlpha; 115 private float mDozeBehindAlpha; 116 private float mCurrentInFrontAlpha = NOT_INITIALIZED; 117 private float mCurrentBehindAlpha = NOT_INITIALIZED; 118 private float mCurrentHeadsUpAlpha = NOT_INITIALIZED; 119 private int mPinnedHeadsUpCount; 120 private float mTopHeadsUpDragAmount; 121 private View mDraggedHeadsUpView; 122 private boolean mForceHideScrims; 123 private boolean mSkipFirstFrame; 124 private boolean mDontAnimateBouncerChanges; 125 private boolean mKeyguardFadingOutInProgress; 126 private boolean mAnimatingDozeUnlock; 127 private ValueAnimator mKeyguardFadeoutAnimation; 128 /** Wake up from AOD transition is starting; need fully opaque front scrim */ 129 private boolean mWakingUpFromAodStarting; 130 /** Wake up from AOD transition is in progress; need black tint */ 131 private boolean mWakingUpFromAodInProgress; 132 /** Wake up from AOD transition is animating; need to reset when animation finishes */ 133 private boolean mWakingUpFromAodAnimationRunning; 134 private boolean mScrimsVisble; 135 private final Consumer<Boolean> mScrimVisibleListener; 136 137 public ScrimController(LightBarController lightBarController, ScrimView scrimBehind, 138 ScrimView scrimInFront, View headsUpScrim, 139 Consumer<Boolean> scrimVisibleListener) { 140 mScrimBehind = scrimBehind; 141 mScrimInFront = scrimInFront; 142 mHeadsUpScrim = headsUpScrim; 143 mScrimVisibleListener = scrimVisibleListener; 144 final Context context = scrimBehind.getContext(); 145 mUnlockMethodCache = UnlockMethodCache.getInstance(context); 146 mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context); 147 mLightBarController = lightBarController; 148 mScrimBehindAlphaResValue = context.getResources().getFloat(R.dimen.scrim_behind_alpha); 149 // Scrim alpha is initially set to the value on the resource but might be changed 150 // to make sure that text on top of it is legible. 151 mScrimBehindAlpha = mScrimBehindAlphaResValue; 152 153 mColorExtractor = Dependency.get(SysuiColorExtractor.class); 154 mColorExtractor.addOnColorsChangedListener(this); 155 mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, 156 ColorExtractor.TYPE_DARK, true /* ignoreVisibility */); 157 mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, 158 ColorExtractor.TYPE_DARK, true /* ignoreVisibility */); 159 mNeedsDrawableColorUpdate = true; 160 161 updateHeadsUpScrim(false); 162 updateScrims(); 163 } 164 165 public void setKeyguardShowing(boolean showing) { 166 mKeyguardShowing = showing; 167 168 // Showing/hiding the keyguard means that scrim colors have to be switched 169 mNeedsDrawableColorUpdate = true; 170 scheduleUpdate(); 171 } 172 173 protected void setScrimBehindValues(float scrimBehindAlphaKeyguard, 174 float scrimBehindAlphaUnlocking) { 175 mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard; 176 mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking; 177 scheduleUpdate(); 178 } 179 180 public void onTrackingStarted() { 181 mTracking = true; 182 mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer(); 183 } 184 185 public void onExpandingFinished() { 186 mTracking = false; 187 } 188 189 public void setPanelExpansion(float fraction) { 190 if (mFraction != fraction) { 191 mFraction = fraction; 192 scheduleUpdate(); 193 if (mPinnedHeadsUpCount != 0) { 194 updateHeadsUpScrim(false); 195 } 196 if (mKeyguardFadeoutAnimation != null && mTracking) { 197 mKeyguardFadeoutAnimation.cancel(); 198 } 199 } 200 } 201 202 public void setBouncerShowing(boolean showing) { 203 mBouncerShowing = showing; 204 mAnimateChange = !mTracking && !mDontAnimateBouncerChanges && !mKeyguardFadingOutInProgress; 205 scheduleUpdate(); 206 } 207 208 /** Prepares the wakeUpFromAod animation (while turning on screen); Forces black scrims. */ 209 public void prepareWakeUpFromAod() { 210 if (mWakingUpFromAodInProgress) { 211 return; 212 } 213 mWakingUpFromAodInProgress = true; 214 mWakingUpFromAodStarting = true; 215 mAnimateChange = false; 216 scheduleUpdate(); 217 onPreDraw(); 218 } 219 220 /** Starts the wakeUpFromAod animation (once screen is on); animate to transparent scrims. */ 221 public void wakeUpFromAod() { 222 if (mWakeAndUnlocking || mAnimateKeyguardFadingOut) { 223 // Wake and unlocking has a separate transition that must not be interfered with. 224 mWakingUpFromAodStarting = false; 225 mWakingUpFromAodInProgress = false; 226 return; 227 } 228 if (mWakingUpFromAodStarting) { 229 mWakingUpFromAodInProgress = true; 230 mWakingUpFromAodStarting = false; 231 mAnimateChange = true; 232 scheduleUpdate(); 233 } 234 } 235 236 public void setWakeAndUnlocking() { 237 mWakeAndUnlocking = true; 238 mAnimatingDozeUnlock = true; 239 mWakingUpFromAodStarting = false; 240 mWakingUpFromAodInProgress = false; 241 scheduleUpdate(); 242 } 243 244 public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished, 245 boolean skipFirstFrame) { 246 mWakeAndUnlocking = false; 247 mAnimateKeyguardFadingOut = true; 248 mDurationOverride = duration; 249 mAnimationDelay = delay; 250 mAnimateChange = true; 251 mSkipFirstFrame = skipFirstFrame; 252 mOnAnimationFinished = onAnimationFinished; 253 254 if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) { 255 scheduleUpdate(); 256 257 // No need to wait for the next frame to be drawn for this case - onPreDraw will execute 258 // the changes we just scheduled. 259 onPreDraw(); 260 } else { 261 262 // In case the user isn't unlocked, make sure to delay a bit because the system is hosed 263 // with too many things in this case, in order to not skip the initial frames. 264 mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16); 265 } 266 } 267 268 public void abortKeyguardFadingOut() { 269 if (mAnimateKeyguardFadingOut) { 270 endAnimateKeyguardFadingOut(true /* force */); 271 } 272 } 273 274 public void animateKeyguardUnoccluding(long duration) { 275 mAnimateChange = false; 276 setScrimBehindAlpha(0f); 277 mAnimateChange = true; 278 scheduleUpdate(); 279 mDurationOverride = duration; 280 } 281 282 public void animateGoingToFullShade(long delay, long duration) { 283 mDurationOverride = duration; 284 mAnimationDelay = delay; 285 mAnimateChange = true; 286 scheduleUpdate(); 287 } 288 289 public void setDozing(boolean dozing) { 290 if (mDozing != dozing) { 291 mDozing = dozing; 292 scheduleUpdate(); 293 } 294 } 295 296 public void setDozeInFrontAlpha(float alpha) { 297 mDozeInFrontAlpha = alpha; 298 updateScrimColor(mScrimInFront); 299 } 300 301 public void setDozeBehindAlpha(float alpha) { 302 mDozeBehindAlpha = alpha; 303 updateScrimColor(mScrimBehind); 304 } 305 306 public float getDozeBehindAlpha() { 307 return mDozeBehindAlpha; 308 } 309 310 public float getDozeInFrontAlpha() { 311 return mDozeInFrontAlpha; 312 } 313 314 public void setNotificationCount(int notificationCount) { 315 final float maxNotificationDensity = 3; 316 float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f); 317 float newAlpha = MathUtils.map(0, 1, 318 GRADIENT_SCRIM_ALPHA, GRADIENT_SCRIM_ALPHA_BUSY, 319 notificationDensity); 320 if (mScrimBehindAlphaKeyguard != newAlpha) { 321 mScrimBehindAlphaKeyguard = newAlpha; 322 mAnimateChange = true; 323 scheduleUpdate(); 324 } 325 } 326 327 private float getScrimInFrontAlpha() { 328 return mKeyguardUpdateMonitor.needsSlowUnlockTransition() 329 ? SCRIM_IN_FRONT_ALPHA_LOCKED 330 : SCRIM_IN_FRONT_ALPHA; 331 } 332 333 /** 334 * Sets the given drawable as the background of the scrim that shows up behind the 335 * notifications. 336 */ 337 public void setScrimBehindDrawable(Drawable drawable) { 338 mScrimBehind.setDrawable(drawable); 339 } 340 341 protected void scheduleUpdate() { 342 if (mUpdatePending) return; 343 344 // Make sure that a frame gets scheduled. 345 mScrimBehind.invalidate(); 346 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 347 mUpdatePending = true; 348 } 349 350 protected void updateScrims() { 351 // Make sure we have the right gradients and their opacities will satisfy GAR. 352 if (mNeedsDrawableColorUpdate) { 353 mNeedsDrawableColorUpdate = false; 354 final GradientColors currentScrimColors; 355 if (mKeyguardShowing) { 356 // Always animate color changes if we're seeing the keyguard 357 mScrimInFront.setColors(mLockColors, true /* animated */); 358 mScrimBehind.setColors(mLockColors, true /* animated */); 359 currentScrimColors = mLockColors; 360 } else { 361 // Only animate scrim color if the scrim view is actually visible 362 boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0; 363 boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0; 364 mScrimInFront.setColors(mSystemColors, animateScrimInFront); 365 mScrimBehind.setColors(mSystemColors, animateScrimBehind); 366 currentScrimColors = mSystemColors; 367 } 368 369 // Calculate minimum scrim opacity for white or black text. 370 int textColor = currentScrimColors.supportsDarkText() ? Color.BLACK : Color.WHITE; 371 int mainColor = currentScrimColors.getMainColor(); 372 float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor, 373 4.5f /* minimumContrast */) / 255f; 374 mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity); 375 mLightBarController.setScrimColor(mScrimInFront.getColors()); 376 } 377 378 if (mAnimateKeyguardFadingOut || mForceHideScrims) { 379 setScrimInFrontAlpha(0f); 380 setScrimBehindAlpha(0f); 381 } else if (mWakeAndUnlocking) { 382 // During wake and unlock, we first hide everything behind a black scrim, which then 383 // gets faded out from animateKeyguardFadingOut. This must never be animated. 384 mAnimateChange = false; 385 if (mDozing) { 386 setScrimInFrontAlpha(0f); 387 setScrimBehindAlpha(1f); 388 } else { 389 setScrimInFrontAlpha(1f); 390 setScrimBehindAlpha(0f); 391 } 392 } else if (!mKeyguardShowing && !mBouncerShowing && !mWakingUpFromAodStarting) { 393 updateScrimNormal(); 394 setScrimInFrontAlpha(0); 395 } else { 396 updateScrimKeyguard(); 397 } 398 mAnimateChange = false; 399 dispatchScrimsVisible(); 400 } 401 402 private void dispatchScrimsVisible() { 403 boolean scrimsVisible = mScrimBehind.getViewAlpha() > 0 || mScrimInFront.getViewAlpha() > 0; 404 405 if (mScrimsVisble != scrimsVisible) { 406 mScrimsVisble = scrimsVisible; 407 408 mScrimVisibleListener.accept(scrimsVisible); 409 } 410 } 411 412 private void updateScrimKeyguard() { 413 if (mTracking && mDarkenWhileDragging) { 414 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 415 float fraction = 1 - behindFraction; 416 fraction = (float) Math.pow(fraction, 0.8f); 417 behindFraction = (float) Math.pow(behindFraction, 0.8f); 418 setScrimInFrontAlpha(fraction * getScrimInFrontAlpha()); 419 setScrimBehindAlpha(behindFraction * mScrimBehindAlphaKeyguard); 420 } else if (mBouncerShowing && !mBouncerIsKeyguard) { 421 setScrimInFrontAlpha(getScrimInFrontAlpha()); 422 updateScrimNormal(); 423 } else if (mBouncerShowing) { 424 setScrimInFrontAlpha(0f); 425 setScrimBehindAlpha(mScrimBehindAlpha); 426 } else { 427 float fraction = Math.max(0, Math.min(mFraction, 1)); 428 if (mWakingUpFromAodStarting) { 429 setScrimInFrontAlpha(1f); 430 } else { 431 setScrimInFrontAlpha(0f); 432 } 433 setScrimBehindAlpha(fraction 434 * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking) 435 + mScrimBehindAlphaUnlocking); 436 } 437 } 438 439 private void updateScrimNormal() { 440 float frac = mFraction; 441 // let's start this 20% of the way down the screen 442 frac = frac * 1.2f - 0.2f; 443 if (frac <= 0) { 444 setScrimBehindAlpha(0); 445 } else { 446 // woo, special effects 447 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 448 setScrimBehindAlpha(k * mScrimBehindAlpha); 449 } 450 } 451 452 private void setScrimBehindAlpha(float alpha) { 453 setScrimAlpha(mScrimBehind, alpha); 454 } 455 456 private void setScrimInFrontAlpha(float alpha) { 457 setScrimAlpha(mScrimInFront, alpha); 458 if (alpha == 0f) { 459 mScrimInFront.setClickable(false); 460 } else { 461 // Eat touch events (unless dozing). 462 mScrimInFront.setClickable(!mDozing); 463 } 464 } 465 466 private void setScrimAlpha(View scrim, float alpha) { 467 updateScrim(mAnimateChange, scrim, alpha, getCurrentScrimAlpha(scrim)); 468 } 469 470 protected float getDozeAlpha(View scrim) { 471 return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha; 472 } 473 474 protected float getCurrentScrimAlpha(View scrim) { 475 return scrim == mScrimBehind ? mCurrentBehindAlpha 476 : scrim == mScrimInFront ? mCurrentInFrontAlpha 477 : mCurrentHeadsUpAlpha; 478 } 479 480 private void setCurrentScrimAlpha(View scrim, float alpha) { 481 if (scrim == mScrimBehind) { 482 mCurrentBehindAlpha = alpha; 483 mLightBarController.setScrimAlpha(mCurrentBehindAlpha); 484 } else if (scrim == mScrimInFront) { 485 mCurrentInFrontAlpha = alpha; 486 } else { 487 alpha = Math.max(0.0f, Math.min(1.0f, alpha)); 488 mCurrentHeadsUpAlpha = alpha; 489 } 490 } 491 492 private void updateScrimColor(View scrim) { 493 float alpha1 = getCurrentScrimAlpha(scrim); 494 if (scrim instanceof ScrimView) { 495 ScrimView scrimView = (ScrimView) scrim; 496 float dozeAlpha = getDozeAlpha(scrim); 497 float alpha = 1 - (1 - alpha1) * (1 - dozeAlpha); 498 alpha = Math.max(0, Math.min(1.0f, alpha)); 499 scrimView.setViewAlpha(alpha); 500 501 Trace.traceCounter(Trace.TRACE_TAG_APP, 502 scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha", 503 (int) (alpha * 255)); 504 505 int dozeTint = Color.TRANSPARENT; 506 507 boolean dozing = mAnimatingDozeUnlock || mDozing; 508 boolean frontScrimDozing = mWakingUpFromAodInProgress; 509 if (dozing || frontScrimDozing && scrim == mScrimInFront) { 510 dozeTint = Color.BLACK; 511 } 512 Trace.traceCounter(Trace.TRACE_TAG_APP, 513 scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint", 514 dozeTint == Color.BLACK ? 1 : 0); 515 516 scrimView.setTint(dozeTint); 517 } else { 518 scrim.setAlpha(alpha1); 519 } 520 dispatchScrimsVisible(); 521 } 522 523 private void startScrimAnimation(final View scrim, float target) { 524 float current = getCurrentScrimAlpha(scrim); 525 ValueAnimator anim = ValueAnimator.ofFloat(current, target); 526 anim.addUpdateListener(animation -> { 527 float alpha = (float) animation.getAnimatedValue(); 528 setCurrentScrimAlpha(scrim, alpha); 529 updateScrimColor(scrim); 530 dispatchScrimsVisible(); 531 }); 532 anim.setInterpolator(getInterpolator()); 533 anim.setStartDelay(mAnimationDelay); 534 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 535 anim.addListener(new AnimatorListenerAdapter() { 536 @Override 537 public void onAnimationEnd(Animator animation) { 538 if (!mDeferFinishedListener && mOnAnimationFinished != null) { 539 mOnAnimationFinished.run(); 540 mOnAnimationFinished = null; 541 } 542 if (mKeyguardFadingOutInProgress) { 543 mKeyguardFadeoutAnimation = null; 544 mKeyguardFadingOutInProgress = false; 545 mAnimatingDozeUnlock = false; 546 } 547 if (mWakingUpFromAodAnimationRunning && !mDeferFinishedListener) { 548 mWakingUpFromAodAnimationRunning = false; 549 mWakingUpFromAodInProgress = false; 550 } 551 scrim.setTag(TAG_KEY_ANIM, null); 552 scrim.setTag(TAG_KEY_ANIM_TARGET, null); 553 dispatchScrimsVisible(); 554 } 555 }); 556 anim.start(); 557 if (mAnimateKeyguardFadingOut) { 558 mKeyguardFadingOutInProgress = true; 559 mKeyguardFadeoutAnimation = anim; 560 } 561 if (mWakingUpFromAodInProgress) { 562 mWakingUpFromAodAnimationRunning = true; 563 } 564 if (mSkipFirstFrame) { 565 anim.setCurrentPlayTime(16); 566 } 567 scrim.setTag(TAG_KEY_ANIM, anim); 568 scrim.setTag(TAG_KEY_ANIM_TARGET, target); 569 } 570 571 protected Interpolator getInterpolator() { 572 if (mAnimateKeyguardFadingOut && mKeyguardUpdateMonitor.needsSlowUnlockTransition()) { 573 return KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED; 574 } else if (mAnimateKeyguardFadingOut) { 575 return KEYGUARD_FADE_OUT_INTERPOLATOR; 576 } else { 577 return mInterpolator; 578 } 579 } 580 581 @Override 582 public boolean onPreDraw() { 583 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 584 mUpdatePending = false; 585 if (mDontAnimateBouncerChanges) { 586 mDontAnimateBouncerChanges = false; 587 } 588 updateScrims(); 589 mDurationOverride = -1; 590 mAnimationDelay = 0; 591 mSkipFirstFrame = false; 592 593 // Make sure that we always call the listener even if we didn't start an animation. 594 endAnimateKeyguardFadingOut(false /* force */); 595 return true; 596 } 597 598 private void endAnimateKeyguardFadingOut(boolean force) { 599 mAnimateKeyguardFadingOut = false; 600 if (force || (!isAnimating(mScrimInFront) && !isAnimating(mScrimBehind))) { 601 if (mOnAnimationFinished != null) { 602 mOnAnimationFinished.run(); 603 mOnAnimationFinished = null; 604 } 605 mKeyguardFadingOutInProgress = false; 606 if (!mWakeAndUnlocking || force) 607 mAnimatingDozeUnlock = false; 608 } 609 } 610 611 private boolean isAnimating(View scrim) { 612 return scrim.getTag(TAG_KEY_ANIM) != null; 613 } 614 615 public void setDrawBehindAsSrc(boolean asSrc) { 616 mScrimBehind.setDrawAsSrc(asSrc); 617 } 618 619 @Override 620 public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { 621 } 622 623 @Override 624 public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { 625 mPinnedHeadsUpCount++; 626 updateHeadsUpScrim(true); 627 } 628 629 @Override 630 public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { 631 mPinnedHeadsUpCount--; 632 if (headsUp == mDraggedHeadsUpView) { 633 mDraggedHeadsUpView = null; 634 mTopHeadsUpDragAmount = 0.0f; 635 } 636 updateHeadsUpScrim(true); 637 } 638 639 @Override 640 public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { 641 } 642 643 private void updateHeadsUpScrim(boolean animate) { 644 updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha(), mCurrentHeadsUpAlpha); 645 } 646 647 private void updateScrim(boolean animate, View scrim, float alpha, float currentAlpha) { 648 if (mKeyguardFadingOutInProgress && mKeyguardFadeoutAnimation.getCurrentPlayTime() != 0) { 649 return; 650 } 651 652 ValueAnimator previousAnimator = ViewState.getChildTag(scrim, 653 TAG_KEY_ANIM); 654 float animEndValue = -1; 655 if (previousAnimator != null) { 656 if (animate || alpha == currentAlpha) { 657 // We are not done yet! Defer calling the finished listener. 658 if (animate) { 659 mDeferFinishedListener = true; 660 } 661 previousAnimator.cancel(); 662 mDeferFinishedListener = false; 663 } else { 664 animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA); 665 } 666 } 667 if (alpha != currentAlpha && alpha != animEndValue) { 668 if (animate) { 669 startScrimAnimation(scrim, alpha); 670 scrim.setTag(TAG_START_ALPHA, currentAlpha); 671 scrim.setTag(TAG_END_ALPHA, alpha); 672 } else { 673 if (previousAnimator != null) { 674 float previousStartValue = ViewState.getChildTag(scrim, TAG_START_ALPHA); 675 float previousEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA); 676 // we need to increase all animation keyframes of the previous animator by the 677 // relative change to the end value 678 PropertyValuesHolder[] values = previousAnimator.getValues(); 679 float relativeDiff = alpha - previousEndValue; 680 float newStartValue = previousStartValue + relativeDiff; 681 newStartValue = Math.max(0, Math.min(1.0f, newStartValue)); 682 values[0].setFloatValues(newStartValue, alpha); 683 scrim.setTag(TAG_START_ALPHA, newStartValue); 684 scrim.setTag(TAG_END_ALPHA, alpha); 685 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 686 } else { 687 // update the alpha directly 688 setCurrentScrimAlpha(scrim, alpha); 689 updateScrimColor(scrim); 690 } 691 } 692 } 693 } 694 695 /** 696 * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means 697 * the heads up is in its resting space and 1 means it's fully dragged out. 698 * 699 * @param draggedHeadsUpView the dragged view 700 * @param topHeadsUpDragAmount how far is it dragged 701 */ 702 public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) { 703 mTopHeadsUpDragAmount = topHeadsUpDragAmount; 704 mDraggedHeadsUpView = draggedHeadsUpView; 705 updateHeadsUpScrim(false); 706 } 707 708 private float calculateHeadsUpAlpha() { 709 float alpha; 710 if (mPinnedHeadsUpCount >= 2) { 711 alpha = 1.0f; 712 } else if (mPinnedHeadsUpCount == 0) { 713 alpha = 0.0f; 714 } else { 715 alpha = 1.0f - mTopHeadsUpDragAmount; 716 } 717 float expandFactor = (1.0f - mFraction); 718 expandFactor = Math.max(expandFactor, 0.0f); 719 return alpha * expandFactor; 720 } 721 722 public void forceHideScrims(boolean hide, boolean animated) { 723 mForceHideScrims = hide; 724 mAnimateChange = animated; 725 scheduleUpdate(); 726 } 727 728 public void dontAnimateBouncerChangesUntilNextFrame() { 729 mDontAnimateBouncerChanges = true; 730 } 731 732 public void setExcludedBackgroundArea(Rect area) { 733 mScrimBehind.setExcludedArea(area); 734 } 735 736 public int getBackgroundColor() { 737 int color = mLockColors.getMainColor(); 738 return Color.argb((int) (mScrimBehind.getAlpha() * Color.alpha(color)), 739 Color.red(color), Color.green(color), Color.blue(color)); 740 } 741 742 public void setScrimBehindChangeRunnable(Runnable changeRunnable) { 743 mScrimBehind.setChangeRunnable(changeRunnable); 744 } 745 746 public void onDensityOrFontScaleChanged() { 747 ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams(); 748 layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize( 749 R.dimen.heads_up_scrim_height); 750 mHeadsUpScrim.setLayoutParams(layoutParams); 751 } 752 753 public void setCurrentUser(int currentUser) { 754 // Don't care in the base class. 755 } 756 757 @Override 758 public void onColorsChanged(ColorExtractor colorExtractor, int which) { 759 if ((which & WallpaperManager.FLAG_LOCK) != 0) { 760 mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, 761 ColorExtractor.TYPE_DARK, true /* ignoreVisibility */); 762 mNeedsDrawableColorUpdate = true; 763 scheduleUpdate(); 764 } 765 if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { 766 mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, 767 ColorExtractor.TYPE_DARK, mKeyguardShowing); 768 mNeedsDrawableColorUpdate = true; 769 scheduleUpdate(); 770 } 771 } 772 773 public void dump(PrintWriter pw) { 774 pw.println(" ScrimController:"); 775 776 pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha()); 777 pw.print(" alpha="); pw.print(mCurrentInFrontAlpha); 778 pw.print(" dozeAlpha="); pw.print(mDozeInFrontAlpha); 779 pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint())); 780 781 pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha()); 782 pw.print(" alpha="); pw.print(mCurrentBehindAlpha); 783 pw.print(" dozeAlpha="); pw.print(mDozeBehindAlpha); 784 pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint())); 785 786 pw.print(" mBouncerShowing="); pw.println(mBouncerShowing); 787 pw.print(" mTracking="); pw.println(mTracking); 788 pw.print(" mForceHideScrims="); pw.println(mForceHideScrims); 789 } 790 } 791