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 static java.lang.Float.isNaN; 20 21 import android.animation.Animator; 22 import android.animation.AnimatorListenerAdapter; 23 import android.animation.ValueAnimator; 24 import android.annotation.IntDef; 25 import android.app.AlarmManager; 26 import android.graphics.Color; 27 import android.os.Handler; 28 import android.os.Trace; 29 import android.util.Log; 30 import android.util.MathUtils; 31 import android.util.Pair; 32 import android.view.View; 33 import android.view.ViewTreeObserver; 34 import android.view.animation.DecelerateInterpolator; 35 import android.view.animation.Interpolator; 36 37 import androidx.annotation.Nullable; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.colorextraction.ColorExtractor.GradientColors; 41 import com.android.internal.graphics.ColorUtils; 42 import com.android.internal.util.function.TriConsumer; 43 import com.android.keyguard.KeyguardUpdateMonitor; 44 import com.android.keyguard.KeyguardUpdateMonitorCallback; 45 import com.android.settingslib.Utils; 46 import com.android.systemui.DejankUtils; 47 import com.android.systemui.Dumpable; 48 import com.android.systemui.R; 49 import com.android.systemui.animation.Interpolators; 50 import com.android.systemui.dagger.SysUISingleton; 51 import com.android.systemui.dagger.qualifiers.Main; 52 import com.android.systemui.dock.DockManager; 53 import com.android.systemui.scrim.ScrimView; 54 import com.android.systemui.statusbar.notification.stack.ViewState; 55 import com.android.systemui.statusbar.policy.ConfigurationController; 56 import com.android.systemui.statusbar.policy.KeyguardStateController; 57 import com.android.systemui.util.AlarmTimeout; 58 import com.android.systemui.util.wakelock.DelayedWakeLock; 59 import com.android.systemui.util.wakelock.WakeLock; 60 61 import java.io.FileDescriptor; 62 import java.io.PrintWriter; 63 import java.lang.annotation.Retention; 64 import java.lang.annotation.RetentionPolicy; 65 import java.util.concurrent.Executor; 66 import java.util.function.Consumer; 67 68 import javax.inject.Inject; 69 70 /** 71 * Controls both the scrim behind the notifications and in front of the notifications (when a 72 * security method gets shown). 73 */ 74 @SysUISingleton 75 public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable { 76 77 static final String TAG = "ScrimController"; 78 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 79 80 /** 81 * General scrim animation duration. 82 */ 83 public static final long ANIMATION_DURATION = 220; 84 /** 85 * Longer duration, currently only used when going to AOD. 86 */ 87 public static final long ANIMATION_DURATION_LONG = 1000; 88 /** 89 * When both scrims have 0 alpha. 90 */ 91 public static final int TRANSPARENT = 0; 92 /** 93 * When scrims aren't transparent (alpha 0) but also not opaque (alpha 1.) 94 */ 95 public static final int SEMI_TRANSPARENT = 1; 96 /** 97 * When at least 1 scrim is fully opaque (alpha set to 1.) 98 */ 99 public static final int OPAQUE = 2; 100 private boolean mClipsQsScrim; 101 102 /** 103 * The amount of progress we are currently in if we're transitioning to the full shade. 104 * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full 105 * shade. 106 */ 107 private float mTransitionToFullShadeProgress; 108 109 /** 110 * If we're currently transitioning to the full shade. 111 */ 112 private boolean mTransitioningToFullShade; 113 114 @IntDef(prefix = {"VISIBILITY_"}, value = { 115 TRANSPARENT, 116 SEMI_TRANSPARENT, 117 OPAQUE 118 }) 119 @Retention(RetentionPolicy.SOURCE) 120 public @interface ScrimVisibility { 121 } 122 123 /** 124 * Default alpha value for most scrims. 125 */ 126 protected static final float KEYGUARD_SCRIM_ALPHA = 0.2f; 127 /** 128 * Scrim opacity when the phone is about to wake-up. 129 */ 130 public static final float WAKE_SENSOR_SCRIM_ALPHA = 0.6f; 131 132 /** 133 * The default scrim under the shade and dialogs. 134 * This should not be lower than 0.54, otherwise we won't pass GAR. 135 */ 136 public static final float BUSY_SCRIM_ALPHA = 1f; 137 138 /** 139 * The default scrim under the expanded bubble stack. 140 * This should not be lower than 0.54, otherwise we won't pass GAR. 141 */ 142 public static final float BUBBLE_SCRIM_ALPHA = 0.6f; 143 144 /** 145 * Scrim opacity that can have text on top. 146 */ 147 public static final float GAR_SCRIM_ALPHA = 0.6f; 148 149 static final int TAG_KEY_ANIM = R.id.scrim; 150 private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; 151 private static final int TAG_END_ALPHA = R.id.scrim_alpha_end; 152 private static final float NOT_INITIALIZED = -1; 153 154 private ScrimState mState = ScrimState.UNINITIALIZED; 155 156 private ScrimView mScrimInFront; 157 private ScrimView mNotificationsScrim; 158 private ScrimView mScrimBehind; 159 @Nullable 160 private ScrimView mScrimForBubble; 161 162 private Runnable mScrimBehindChangeRunnable; 163 164 private final KeyguardStateController mKeyguardStateController; 165 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 166 private final DozeParameters mDozeParameters; 167 private final DockManager mDockManager; 168 private final AlarmTimeout mTimeTicker; 169 private final KeyguardVisibilityCallback mKeyguardVisibilityCallback; 170 private final Handler mHandler; 171 private final Executor mMainExecutor; 172 private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; 173 174 private GradientColors mColors; 175 private boolean mNeedsDrawableColorUpdate; 176 177 private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA; 178 private final float mDefaultScrimAlpha; 179 180 // Assuming the shade is expanded during initialization 181 private float mPanelExpansion = 1f; 182 private float mQsExpansion; 183 private boolean mQsBottomVisible; 184 185 private boolean mDarkenWhileDragging; 186 private boolean mExpansionAffectsAlpha = true; 187 private boolean mAnimateChange; 188 private boolean mUpdatePending; 189 private boolean mTracking; 190 private long mAnimationDuration = -1; 191 private long mAnimationDelay; 192 private Animator.AnimatorListener mAnimatorListener; 193 private final Interpolator mInterpolator = new DecelerateInterpolator(); 194 195 private float mInFrontAlpha = NOT_INITIALIZED; 196 private float mBehindAlpha = NOT_INITIALIZED; 197 private float mNotificationsAlpha = NOT_INITIALIZED; 198 private float mBubbleAlpha = NOT_INITIALIZED; 199 200 private int mInFrontTint; 201 private int mBehindTint; 202 private int mNotificationsTint; 203 private int mBubbleTint; 204 205 private boolean mWallpaperVisibilityTimedOut; 206 private int mScrimsVisibility; 207 private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener; 208 private Consumer<Integer> mScrimVisibleListener; 209 private boolean mBlankScreen; 210 private boolean mScreenBlankingCallbackCalled; 211 private Callback mCallback; 212 private boolean mWallpaperSupportsAmbientMode; 213 private boolean mScreenOn; 214 215 // Scrim blanking callbacks 216 private Runnable mPendingFrameCallback; 217 private Runnable mBlankingTransitionRunnable; 218 219 private final WakeLock mWakeLock; 220 private boolean mWakeLockHeld; 221 private boolean mKeyguardOccluded; 222 223 @Inject ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, ConfigurationController configurationController, @Main Executor mainExecutor, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController)224 public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, 225 AlarmManager alarmManager, KeyguardStateController keyguardStateController, 226 DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, 227 KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, 228 ConfigurationController configurationController, @Main Executor mainExecutor, 229 UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) { 230 mScrimStateListener = lightBarController::setScrimState; 231 mDefaultScrimAlpha = BUSY_SCRIM_ALPHA; 232 ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(BUBBLE_SCRIM_ALPHA); 233 234 mKeyguardStateController = keyguardStateController; 235 mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); 236 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 237 mKeyguardVisibilityCallback = new KeyguardVisibilityCallback(); 238 mHandler = handler; 239 mMainExecutor = mainExecutor; 240 mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; 241 mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout, 242 "hide_aod_wallpaper", mHandler); 243 mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build(); 244 // Scrim alpha is initially set to the value on the resource but might be changed 245 // to make sure that text on top of it is legible. 246 mDozeParameters = dozeParameters; 247 mDockManager = dockManager; 248 keyguardStateController.addCallback(new KeyguardStateController.Callback() { 249 @Override 250 public void onKeyguardFadingAwayChanged() { 251 setKeyguardFadingAway(keyguardStateController.isKeyguardFadingAway(), 252 keyguardStateController.getKeyguardFadingAwayDuration()); 253 } 254 }); 255 configurationController.addCallback(new ConfigurationController.ConfigurationListener() { 256 @Override 257 public void onThemeChanged() { 258 ScrimController.this.onThemeChanged(); 259 } 260 261 @Override 262 public void onOverlayChanged() { 263 ScrimController.this.onThemeChanged(); 264 } 265 266 @Override 267 public void onUiModeChanged() { 268 ScrimController.this.onThemeChanged(); 269 } 270 }); 271 272 mColors = new GradientColors(); 273 } 274 275 /** 276 * Attach the controller to the supplied views. 277 */ attachViews(ScrimView behindScrim, ScrimView notificationsScrim, ScrimView scrimInFront, @Nullable ScrimView scrimForBubble)278 public void attachViews(ScrimView behindScrim, ScrimView notificationsScrim, 279 ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) { 280 mNotificationsScrim = notificationsScrim; 281 mScrimBehind = behindScrim; 282 mScrimInFront = scrimInFront; 283 mScrimForBubble = scrimForBubble; 284 updateThemeColors(); 285 286 behindScrim.enableBottomEdgeConcave(mClipsQsScrim); 287 mNotificationsScrim.enableRoundedCorners(true); 288 289 if (mScrimBehindChangeRunnable != null) { 290 mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor); 291 mScrimBehindChangeRunnable = null; 292 } 293 294 final ScrimState[] states = ScrimState.values(); 295 for (int i = 0; i < states.length; i++) { 296 states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters, 297 mDockManager); 298 states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard); 299 states[i].setDefaultScrimAlpha(mDefaultScrimAlpha); 300 } 301 302 mScrimBehind.setDefaultFocusHighlightEnabled(false); 303 mNotificationsScrim.setDefaultFocusHighlightEnabled(false); 304 mScrimInFront.setDefaultFocusHighlightEnabled(false); 305 if (mScrimForBubble != null) { 306 mScrimForBubble.setDefaultFocusHighlightEnabled(false); 307 } 308 updateScrims(); 309 mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); 310 } 311 312 /** 313 * Sets corner radius of scrims. 314 */ setScrimCornerRadius(int radius)315 public void setScrimCornerRadius(int radius) { 316 if (mScrimBehind == null || mNotificationsScrim == null) { 317 return; 318 } 319 mScrimBehind.setCornerRadius(radius); 320 mNotificationsScrim.setCornerRadius(radius); 321 } 322 setScrimVisibleListener(Consumer<Integer> listener)323 void setScrimVisibleListener(Consumer<Integer> listener) { 324 mScrimVisibleListener = listener; 325 } 326 transitionTo(ScrimState state)327 public void transitionTo(ScrimState state) { 328 transitionTo(state, null); 329 } 330 transitionTo(ScrimState state, Callback callback)331 public void transitionTo(ScrimState state, Callback callback) { 332 if (state == mState) { 333 // Call the callback anyway, unless it's already enqueued 334 if (callback != null && mCallback != callback) { 335 callback.onFinished(); 336 } 337 return; 338 } else if (DEBUG) { 339 Log.d(TAG, "State changed to: " + state); 340 } 341 342 if (state == ScrimState.UNINITIALIZED) { 343 throw new IllegalArgumentException("Cannot change to UNINITIALIZED."); 344 } 345 346 final ScrimState oldState = mState; 347 mState = state; 348 Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal()); 349 350 if (mCallback != null) { 351 mCallback.onCancelled(); 352 } 353 mCallback = callback; 354 355 state.prepare(oldState); 356 mScreenBlankingCallbackCalled = false; 357 mAnimationDelay = 0; 358 mBlankScreen = state.getBlanksScreen(); 359 mAnimateChange = state.getAnimateChange(); 360 mAnimationDuration = state.getAnimationDuration(); 361 362 mInFrontTint = state.getFrontTint(); 363 mBehindTint = state.getBehindTint(); 364 mNotificationsTint = state.getNotifTint(); 365 mBubbleTint = state.getBubbleTint(); 366 367 mInFrontAlpha = state.getFrontAlpha(); 368 mBehindAlpha = state.getBehindAlpha(); 369 mBubbleAlpha = state.getBubbleAlpha(); 370 mNotificationsAlpha = state.getNotifAlpha(); 371 if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { 372 throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: " 373 + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " 374 + mNotificationsAlpha); 375 } 376 applyStateToAlpha(); 377 378 // Scrim might acquire focus when user is navigating with a D-pad or a keyboard. 379 // We need to disable focus otherwise AOD would end up with a gray overlay. 380 mScrimInFront.setFocusable(!state.isLowPowerState()); 381 mScrimBehind.setFocusable(!state.isLowPowerState()); 382 mNotificationsScrim.setFocusable(!state.isLowPowerState()); 383 384 // Cancel blanking transitions that were pending before we requested a new state 385 if (mPendingFrameCallback != null) { 386 mScrimBehind.removeCallbacks(mPendingFrameCallback); 387 mPendingFrameCallback = null; 388 } 389 if (mHandler.hasCallbacks(mBlankingTransitionRunnable)) { 390 mHandler.removeCallbacks(mBlankingTransitionRunnable); 391 mBlankingTransitionRunnable = null; 392 } 393 394 // Showing/hiding the keyguard means that scrim colors have to be switched, not necessary 395 // to do the same when you're just showing the brightness mirror. 396 mNeedsDrawableColorUpdate = state != ScrimState.BRIGHTNESS_MIRROR; 397 398 // The device might sleep if it's entering AOD, we need to make sure that 399 // the animation plays properly until the last frame. 400 // It's important to avoid holding the wakelock unless necessary because 401 // WakeLock#aqcuire will trigger an IPC and will cause jank. 402 if (mState.isLowPowerState()) { 403 holdWakeLock(); 404 } 405 406 // AOD wallpapers should fade away after a while. 407 // Docking pulses may take a long time, wallpapers should also fade away after a while. 408 mWallpaperVisibilityTimedOut = false; 409 if (shouldFadeAwayWallpaper()) { 410 DejankUtils.postAfterTraversal(() -> { 411 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), 412 AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); 413 }); 414 } else { 415 DejankUtils.postAfterTraversal(mTimeTicker::cancel); 416 } 417 418 if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) { 419 mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY; 420 scheduleUpdate(); 421 } else if ((oldState == ScrimState.AOD // leaving doze 422 && (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED)) 423 || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) { 424 // Scheduling a frame isn't enough when: 425 // • Leaving doze and we need to modify scrim color immediately 426 // • ColorFade will not kick-in and scrim cannot wait for pre-draw. 427 onPreDraw(); 428 } else { 429 // Schedule a frame 430 scheduleUpdate(); 431 } 432 433 dispatchBackScrimState(mScrimBehind.getViewAlpha()); 434 } 435 shouldFadeAwayWallpaper()436 private boolean shouldFadeAwayWallpaper() { 437 if (!mWallpaperSupportsAmbientMode) { 438 return false; 439 } 440 441 if (mState == ScrimState.AOD 442 && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) { 443 return true; 444 } 445 446 return false; 447 } 448 getState()449 public ScrimState getState() { 450 return mState; 451 } 452 setScrimBehindValues(float scrimBehindAlphaKeyguard)453 protected void setScrimBehindValues(float scrimBehindAlphaKeyguard) { 454 mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard; 455 ScrimState[] states = ScrimState.values(); 456 for (int i = 0; i < states.length; i++) { 457 states[i].setScrimBehindAlphaKeyguard(scrimBehindAlphaKeyguard); 458 } 459 scheduleUpdate(); 460 } 461 onTrackingStarted()462 public void onTrackingStarted() { 463 mTracking = true; 464 mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); 465 } 466 onExpandingFinished()467 public void onExpandingFinished() { 468 mTracking = false; 469 } 470 471 @VisibleForTesting onHideWallpaperTimeout()472 protected void onHideWallpaperTimeout() { 473 if (mState != ScrimState.AOD && mState != ScrimState.PULSING) { 474 return; 475 } 476 477 holdWakeLock(); 478 mWallpaperVisibilityTimedOut = true; 479 mAnimateChange = true; 480 mAnimationDuration = mDozeParameters.getWallpaperFadeOutDuration(); 481 scheduleUpdate(); 482 } 483 holdWakeLock()484 private void holdWakeLock() { 485 if (!mWakeLockHeld) { 486 if (mWakeLock != null) { 487 mWakeLockHeld = true; 488 mWakeLock.acquire(TAG); 489 } else { 490 Log.w(TAG, "Cannot hold wake lock, it has not been set yet"); 491 } 492 } 493 } 494 495 /** 496 * Current state of the shade expansion when pulling it from the top. 497 * This value is 1 when on top of the keyguard and goes to 0 as the user drags up. 498 * 499 * The expansion fraction is tied to the scrim opacity. 500 * 501 * @param fraction From 0 to 1 where 0 means collapsed and 1 expanded. 502 */ setPanelExpansion(float fraction)503 public void setPanelExpansion(float fraction) { 504 if (isNaN(fraction)) { 505 throw new IllegalArgumentException("Fraction should not be NaN"); 506 } 507 if (mPanelExpansion != fraction) { 508 mPanelExpansion = fraction; 509 510 boolean relevantState = (mState == ScrimState.UNLOCKED 511 || mState == ScrimState.KEYGUARD 512 || mState == ScrimState.SHADE_LOCKED 513 || mState == ScrimState.PULSING 514 || mState == ScrimState.BUBBLE_EXPANDED); 515 if (!(relevantState && mExpansionAffectsAlpha)) { 516 return; 517 } 518 applyAndDispatchState(); 519 } 520 } 521 522 /** 523 * Set the amount of progress we are currently in if we're transitioning to the full shade. 524 * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full 525 * shade. 526 */ setTransitionToFullShadeProgress(float progress)527 public void setTransitionToFullShadeProgress(float progress) { 528 if (progress != mTransitionToFullShadeProgress) { 529 mTransitionToFullShadeProgress = progress; 530 setTransitionToFullShade(progress > 0.0f); 531 applyAndDispatchState(); 532 } 533 } 534 535 /** 536 * Set if we're currently transitioning to the full shade 537 */ setTransitionToFullShade(boolean transitioning)538 private void setTransitionToFullShade(boolean transitioning) { 539 if (transitioning != mTransitioningToFullShade) { 540 mTransitioningToFullShade = transitioning; 541 if (transitioning) { 542 // Let's make sure the shade locked is ready 543 ScrimState.SHADE_LOCKED.prepare(mState); 544 } 545 } 546 } 547 548 549 /** 550 * Set bounds for notifications background, all coordinates are absolute 551 */ setNotificationsBounds(float left, float top, float right, float bottom)552 public void setNotificationsBounds(float left, float top, float right, float bottom) { 553 if (mClipsQsScrim) { 554 // notification scrim's rounded corners are anti-aliased, but clipping of the QS/behind 555 // scrim can't be and it's causing jagged corners. That's why notification scrim needs 556 // to overlap QS scrim by one pixel horizontally (left - 1 and right + 1) 557 // see: b/186644628 558 mNotificationsScrim.setDrawableBounds(left - 1, top, right + 1, bottom); 559 mScrimBehind.setBottomEdgePosition((int) top); 560 } else { 561 mNotificationsScrim.setDrawableBounds(left, top, right, bottom); 562 } 563 } 564 565 /** 566 * Current state of the QuickSettings when pulling it from the top. 567 * 568 * @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded. 569 * @param qsPanelBottomY Absolute Y position of qs panel bottom 570 */ setQsPosition(float expansionFraction, int qsPanelBottomY)571 public void setQsPosition(float expansionFraction, int qsPanelBottomY) { 572 if (isNaN(expansionFraction)) { 573 return; 574 } 575 boolean qsBottomVisible = qsPanelBottomY > 0; 576 if (mQsExpansion != expansionFraction || mQsBottomVisible != qsBottomVisible) { 577 mQsExpansion = expansionFraction; 578 mQsBottomVisible = qsBottomVisible; 579 boolean relevantState = (mState == ScrimState.SHADE_LOCKED 580 || mState == ScrimState.KEYGUARD 581 || mState == ScrimState.PULSING 582 || mState == ScrimState.BUBBLE_EXPANDED); 583 if (!(relevantState && mExpansionAffectsAlpha)) { 584 return; 585 } 586 applyAndDispatchState(); 587 } 588 } 589 590 /** 591 * If QS and notification scrims should not overlap, and should be clipped to each other's 592 * bounds instead. 593 */ setClipsQsScrim(boolean clipScrim)594 public void setClipsQsScrim(boolean clipScrim) { 595 if (clipScrim == mClipsQsScrim) { 596 return; 597 } 598 mClipsQsScrim = clipScrim; 599 for (ScrimState state : ScrimState.values()) { 600 state.setClipQsScrim(mClipsQsScrim); 601 } 602 if (mScrimBehind != null) { 603 mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim); 604 } 605 if (mState != ScrimState.UNINITIALIZED) { 606 // the clipScrimState has changed, let's reprepare ourselves 607 mState.prepare(mState); 608 applyAndDispatchState(); 609 } 610 } 611 612 @VisibleForTesting getClipQsScrim()613 public boolean getClipQsScrim() { 614 return mClipsQsScrim; 615 } 616 setOrAdaptCurrentAnimation(@ullable View scrim)617 private void setOrAdaptCurrentAnimation(@Nullable View scrim) { 618 if (scrim == null) { 619 return; 620 } 621 622 float alpha = getCurrentScrimAlpha(scrim); 623 boolean qsScrimPullingDown = scrim == mScrimBehind && mQsBottomVisible; 624 if (isAnimating(scrim) && !qsScrimPullingDown) { 625 // Adapt current animation. 626 ValueAnimator previousAnimator = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM); 627 float previousEndValue = (Float) scrim.getTag(TAG_END_ALPHA); 628 float previousStartValue = (Float) scrim.getTag(TAG_START_ALPHA); 629 float relativeDiff = alpha - previousEndValue; 630 float newStartValue = previousStartValue + relativeDiff; 631 scrim.setTag(TAG_START_ALPHA, newStartValue); 632 scrim.setTag(TAG_END_ALPHA, alpha); 633 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 634 } else { 635 // Set animation. 636 updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); 637 } 638 } 639 applyStateToAlpha()640 private void applyStateToAlpha() { 641 if (!mExpansionAffectsAlpha) { 642 return; 643 } 644 645 if (mState == ScrimState.UNLOCKED || mState == ScrimState.BUBBLE_EXPANDED) { 646 // Darken scrim as you pull down the shade when unlocked, unless the shade is expanding 647 // because we're doing the screen off animation. 648 if (!mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) { 649 float behindFraction = getInterpolatedFraction(); 650 behindFraction = (float) Math.pow(behindFraction, 0.8f); 651 if (mClipsQsScrim) { 652 mBehindAlpha = 1; 653 mNotificationsAlpha = behindFraction * mDefaultScrimAlpha; 654 } else { 655 mBehindAlpha = behindFraction * mDefaultScrimAlpha; 656 mNotificationsAlpha = mBehindAlpha; 657 } 658 mInFrontAlpha = 0; 659 } 660 } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED 661 || mState == ScrimState.PULSING) { 662 Pair<Integer, Float> result = calculateBackStateForState(mState); 663 int behindTint = result.first; 664 float behindAlpha = result.second; 665 if (mTransitionToFullShadeProgress > 0.0f) { 666 Pair<Integer, Float> shadeResult = calculateBackStateForState( 667 ScrimState.SHADE_LOCKED); 668 behindAlpha = MathUtils.lerp(behindAlpha, shadeResult.second, 669 mTransitionToFullShadeProgress); 670 behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first, 671 mTransitionToFullShadeProgress); 672 } 673 mInFrontAlpha = mState.getFrontAlpha(); 674 if (mClipsQsScrim) { 675 mNotificationsAlpha = behindAlpha; 676 mNotificationsTint = behindTint; 677 mBehindAlpha = 1; 678 mBehindTint = Color.BLACK; 679 } else { 680 mBehindAlpha = behindAlpha; 681 if (mState == ScrimState.SHADE_LOCKED) { 682 // going from KEYGUARD to SHADE_LOCKED state 683 mNotificationsAlpha = getInterpolatedFraction(); 684 } else { 685 mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); 686 } 687 if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) { 688 // Interpolate the notification alpha when transitioning! 689 mNotificationsAlpha = MathUtils.lerp( 690 mNotificationsAlpha, 691 getInterpolatedFraction(), 692 mTransitionToFullShadeProgress); 693 } 694 mNotificationsTint = mState.getNotifTint(); 695 mBehindTint = behindTint; 696 } 697 } 698 if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { 699 throw new IllegalStateException("Scrim opacity is NaN for state: " + mState 700 + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " 701 + mNotificationsAlpha); 702 } 703 } 704 calculateBackStateForState(ScrimState state)705 private Pair<Integer, Float> calculateBackStateForState(ScrimState state) { 706 // Either darken of make the scrim transparent when you 707 // pull down the shade 708 float interpolatedFract = getInterpolatedFraction(); 709 float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha(); 710 float behindAlpha; 711 int behindTint; 712 if (mDarkenWhileDragging) { 713 behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind, 714 interpolatedFract); 715 } else { 716 behindAlpha = MathUtils.lerp(0 /* start */, stateBehind, 717 interpolatedFract); 718 } 719 if (mClipsQsScrim) { 720 behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(), 721 state.getNotifTint(), interpolatedFract); 722 } else { 723 behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), 724 state.getBehindTint(), interpolatedFract); 725 } 726 if (mQsExpansion > 0) { 727 behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion); 728 int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint() 729 : ScrimState.SHADE_LOCKED.getBehindTint(); 730 behindTint = ColorUtils.blendARGB(behindTint, stateTint, mQsExpansion); 731 } 732 return new Pair<>(behindTint, behindAlpha); 733 } 734 735 applyAndDispatchState()736 private void applyAndDispatchState() { 737 applyStateToAlpha(); 738 if (mUpdatePending) { 739 return; 740 } 741 setOrAdaptCurrentAnimation(mScrimBehind); 742 setOrAdaptCurrentAnimation(mNotificationsScrim); 743 setOrAdaptCurrentAnimation(mScrimInFront); 744 setOrAdaptCurrentAnimation(mScrimForBubble); 745 dispatchBackScrimState(mScrimBehind.getViewAlpha()); 746 747 // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING 748 // and docking. 749 if (mWallpaperVisibilityTimedOut) { 750 mWallpaperVisibilityTimedOut = false; 751 DejankUtils.postAfterTraversal(() -> { 752 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), 753 AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); 754 }); 755 } 756 } 757 758 /** 759 * Sets the front scrim opacity in AOD so it's not as bright. 760 * <p> 761 * Displays usually don't support multiple dimming settings when in low power mode. 762 * The workaround is to modify the front scrim opacity when in AOD, so it's not as 763 * bright when you're at the movies or lying down on bed. 764 * <p> 765 * This value will be lost during transitions and only updated again after the the 766 * device is dozing when the light sensor is on. 767 */ setAodFrontScrimAlpha(float alpha)768 public void setAodFrontScrimAlpha(float alpha) { 769 if (mInFrontAlpha != alpha && shouldUpdateFrontScrimAlpha()) { 770 mInFrontAlpha = alpha; 771 updateScrims(); 772 } 773 774 mState.AOD.setAodFrontScrimAlpha(alpha); 775 mState.PULSING.setAodFrontScrimAlpha(alpha); 776 } 777 shouldUpdateFrontScrimAlpha()778 private boolean shouldUpdateFrontScrimAlpha() { 779 if (mState == ScrimState.AOD 780 && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) { 781 return true; 782 } 783 784 if (mState == ScrimState.PULSING) { 785 return true; 786 } 787 788 return false; 789 } 790 791 /** 792 * If the lock screen sensor is active. 793 */ setWakeLockScreenSensorActive(boolean active)794 public void setWakeLockScreenSensorActive(boolean active) { 795 for (ScrimState state : ScrimState.values()) { 796 state.setWakeLockScreenSensorActive(active); 797 } 798 799 if (mState == ScrimState.PULSING) { 800 float newBehindAlpha = mState.getBehindAlpha(); 801 if (mBehindAlpha != newBehindAlpha) { 802 mBehindAlpha = newBehindAlpha; 803 if (isNaN(mBehindAlpha)) { 804 throw new IllegalStateException("Scrim opacity is NaN for state: " + mState 805 + ", back: " + mBehindAlpha); 806 } 807 updateScrims(); 808 } 809 } 810 } 811 scheduleUpdate()812 protected void scheduleUpdate() { 813 if (mUpdatePending || mScrimBehind == null) return; 814 815 // Make sure that a frame gets scheduled. 816 mScrimBehind.invalidate(); 817 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 818 mUpdatePending = true; 819 } 820 updateScrims()821 protected void updateScrims() { 822 // Make sure we have the right gradients and their opacities will satisfy GAR. 823 if (mNeedsDrawableColorUpdate) { 824 mNeedsDrawableColorUpdate = false; 825 // Only animate scrim color if the scrim view is actually visible 826 boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen; 827 boolean animateBehindScrim = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen; 828 boolean animateScrimNotifications = mNotificationsScrim.getViewAlpha() != 0 829 && !mBlankScreen; 830 831 mScrimInFront.setColors(mColors, animateScrimInFront); 832 mScrimBehind.setColors(mColors, animateBehindScrim); 833 mNotificationsScrim.setColors(mColors, animateScrimNotifications); 834 835 dispatchBackScrimState(mScrimBehind.getViewAlpha()); 836 } 837 838 // We want to override the back scrim opacity for the AOD state 839 // when it's time to fade the wallpaper away. 840 boolean aodWallpaperTimeout = (mState == ScrimState.AOD || mState == ScrimState.PULSING) 841 && mWallpaperVisibilityTimedOut; 842 // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim. 843 boolean occludedKeyguard = (mState == ScrimState.PULSING || mState == ScrimState.AOD) 844 && mKeyguardOccluded; 845 if (aodWallpaperTimeout || occludedKeyguard) { 846 mBehindAlpha = 1; 847 } 848 setScrimAlpha(mScrimInFront, mInFrontAlpha); 849 setScrimAlpha(mScrimBehind, mBehindAlpha); 850 setScrimAlpha(mNotificationsScrim, mNotificationsAlpha); 851 852 if (mScrimForBubble != null) { 853 boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen; 854 mScrimForBubble.setColors(mColors, animateScrimForBubble); 855 setScrimAlpha(mScrimForBubble, mBubbleAlpha); 856 } 857 // The animation could have all already finished, let's call onFinished just in case 858 onFinished(mState); 859 dispatchScrimsVisible(); 860 } 861 dispatchBackScrimState(float alpha)862 private void dispatchBackScrimState(float alpha) { 863 // When clipping QS, the notification scrim is the one that feels behind. 864 // mScrimBehind will be drawing black and its opacity will always be 1. 865 if (mClipsQsScrim && mQsBottomVisible) { 866 alpha = mNotificationsAlpha; 867 } 868 mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors()); 869 } 870 dispatchScrimsVisible()871 private void dispatchScrimsVisible() { 872 final ScrimView backScrim = mClipsQsScrim ? mNotificationsScrim : mScrimBehind; 873 final int currentScrimVisibility; 874 if (mScrimInFront.getViewAlpha() == 1 || backScrim.getViewAlpha() == 1) { 875 currentScrimVisibility = OPAQUE; 876 } else if (mScrimInFront.getViewAlpha() == 0 && backScrim.getViewAlpha() == 0) { 877 currentScrimVisibility = TRANSPARENT; 878 } else { 879 currentScrimVisibility = SEMI_TRANSPARENT; 880 } 881 882 if (mScrimsVisibility != currentScrimVisibility) { 883 mScrimsVisibility = currentScrimVisibility; 884 mScrimVisibleListener.accept(currentScrimVisibility); 885 } 886 } 887 getInterpolatedFraction()888 private float getInterpolatedFraction() { 889 return Interpolators.getNotificationScrimAlpha(mPanelExpansion, false /* notification */); 890 } 891 setScrimAlpha(ScrimView scrim, float alpha)892 private void setScrimAlpha(ScrimView scrim, float alpha) { 893 if (alpha == 0f) { 894 scrim.setClickable(false); 895 } else { 896 // Eat touch events (unless dozing). 897 scrim.setClickable(mState != ScrimState.AOD); 898 } 899 updateScrim(scrim, alpha); 900 } 901 getScrimName(ScrimView scrim)902 private String getScrimName(ScrimView scrim) { 903 if (scrim == mScrimInFront) { 904 return "front_scrim"; 905 } else if (scrim == mScrimBehind) { 906 return "behind_scrim"; 907 } else if (scrim == mNotificationsScrim) { 908 return "notifications_scrim"; 909 } else if (scrim == mScrimForBubble) { 910 return "bubble_scrim"; 911 } 912 return "unknown_scrim"; 913 } 914 updateScrimColor(View scrim, float alpha, int tint)915 private void updateScrimColor(View scrim, float alpha, int tint) { 916 alpha = Math.max(0, Math.min(1.0f, alpha)); 917 if (scrim instanceof ScrimView) { 918 ScrimView scrimView = (ScrimView) scrim; 919 920 Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_alpha", 921 (int) (alpha * 255)); 922 923 Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint", 924 Color.alpha(tint)); 925 scrimView.setTint(tint); 926 scrimView.setViewAlpha(alpha); 927 } else { 928 scrim.setAlpha(alpha); 929 } 930 dispatchScrimsVisible(); 931 } 932 startScrimAnimation(final View scrim, float current)933 private void startScrimAnimation(final View scrim, float current) { 934 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 935 if (mAnimatorListener != null) { 936 anim.addListener(mAnimatorListener); 937 } 938 final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() : 939 Color.TRANSPARENT; 940 anim.addUpdateListener(animation -> { 941 final float startAlpha = (Float) scrim.getTag(TAG_START_ALPHA); 942 final float animAmount = (float) animation.getAnimatedValue(); 943 final int finalScrimTint = getCurrentScrimTint(scrim); 944 final float finalScrimAlpha = getCurrentScrimAlpha(scrim); 945 float alpha = MathUtils.lerp(startAlpha, finalScrimAlpha, animAmount); 946 alpha = MathUtils.constrain(alpha, 0f, 1f); 947 int tint = ColorUtils.blendARGB(initialScrimTint, finalScrimTint, animAmount); 948 updateScrimColor(scrim, alpha, tint); 949 dispatchScrimsVisible(); 950 }); 951 anim.setInterpolator(mInterpolator); 952 anim.setStartDelay(mAnimationDelay); 953 anim.setDuration(mAnimationDuration); 954 anim.addListener(new AnimatorListenerAdapter() { 955 private final ScrimState mLastState = mState; 956 private final Callback mLastCallback = mCallback; 957 958 @Override 959 public void onAnimationEnd(Animator animation) { 960 scrim.setTag(TAG_KEY_ANIM, null); 961 onFinished(mLastCallback, mLastState); 962 963 dispatchScrimsVisible(); 964 } 965 }); 966 967 // Cache alpha values because we might want to update this animator in the future if 968 // the user expands the panel while the animation is still running. 969 scrim.setTag(TAG_START_ALPHA, current); 970 scrim.setTag(TAG_END_ALPHA, getCurrentScrimAlpha(scrim)); 971 972 scrim.setTag(TAG_KEY_ANIM, anim); 973 anim.start(); 974 } 975 getCurrentScrimAlpha(View scrim)976 private float getCurrentScrimAlpha(View scrim) { 977 if (scrim == mScrimInFront) { 978 return mInFrontAlpha; 979 } else if (scrim == mScrimBehind) { 980 return mBehindAlpha; 981 } else if (scrim == mNotificationsScrim) { 982 return mNotificationsAlpha; 983 } else if (scrim == mScrimForBubble) { 984 return mBubbleAlpha; 985 } else { 986 throw new IllegalArgumentException("Unknown scrim view"); 987 } 988 } 989 getCurrentScrimTint(View scrim)990 private int getCurrentScrimTint(View scrim) { 991 if (scrim == mScrimInFront) { 992 return mInFrontTint; 993 } else if (scrim == mScrimBehind) { 994 return mBehindTint; 995 } else if (scrim == mNotificationsScrim) { 996 return mNotificationsTint; 997 } else if (scrim == mScrimForBubble) { 998 return mBubbleTint; 999 } else { 1000 throw new IllegalArgumentException("Unknown scrim view"); 1001 } 1002 } 1003 1004 @Override onPreDraw()1005 public boolean onPreDraw() { 1006 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 1007 mUpdatePending = false; 1008 if (mCallback != null) { 1009 mCallback.onStart(); 1010 } 1011 updateScrims(); 1012 return true; 1013 } 1014 1015 /** 1016 * @param state that finished 1017 */ onFinished(ScrimState state)1018 private void onFinished(ScrimState state) { 1019 onFinished(mCallback, state); 1020 } 1021 onFinished(Callback callback, ScrimState state)1022 private void onFinished(Callback callback, ScrimState state) { 1023 if (mPendingFrameCallback != null) { 1024 // No animations can finish while we're waiting on the blanking to finish 1025 return; 1026 1027 } 1028 if (isAnimating(mScrimBehind) 1029 || isAnimating(mNotificationsScrim) 1030 || isAnimating(mScrimInFront) 1031 || isAnimating(mScrimForBubble)) { 1032 if (callback != null && callback != mCallback) { 1033 // Since we only notify the callback that we're finished once everything has 1034 // finished, we need to make sure that any changing callbacks are also invoked 1035 callback.onFinished(); 1036 } 1037 return; 1038 } 1039 if (mWakeLockHeld) { 1040 mWakeLock.release(TAG); 1041 mWakeLockHeld = false; 1042 } 1043 1044 if (callback != null) { 1045 callback.onFinished(); 1046 1047 if (callback == mCallback) { 1048 mCallback = null; 1049 } 1050 } 1051 1052 // When unlocking with fingerprint, we'll fade the scrims from black to transparent. 1053 // At the end of the animation we need to remove the tint. 1054 if (state == ScrimState.UNLOCKED) { 1055 mInFrontTint = Color.TRANSPARENT; 1056 mBehindTint = mState.getBehindTint(); 1057 mNotificationsTint = mState.getNotifTint(); 1058 mBubbleTint = Color.TRANSPARENT; 1059 updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint); 1060 updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint); 1061 updateScrimColor(mNotificationsScrim, mNotificationsAlpha, mNotificationsTint); 1062 if (mScrimForBubble != null) { 1063 updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint); 1064 } 1065 } 1066 } 1067 isAnimating(@ullable View scrim)1068 private boolean isAnimating(@Nullable View scrim) { 1069 return scrim != null && scrim.getTag(TAG_KEY_ANIM) != null; 1070 } 1071 1072 @VisibleForTesting setAnimatorListener(Animator.AnimatorListener animatorListener)1073 void setAnimatorListener(Animator.AnimatorListener animatorListener) { 1074 mAnimatorListener = animatorListener; 1075 } 1076 updateScrim(ScrimView scrim, float alpha)1077 private void updateScrim(ScrimView scrim, float alpha) { 1078 final float currentAlpha = scrim.getViewAlpha(); 1079 1080 ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM); 1081 if (previousAnimator != null) { 1082 // Previous animators should always be cancelled. Not doing so would cause 1083 // overlap, especially on states that don't animate, leading to flickering, 1084 // and in the worst case, an internal state that doesn't represent what 1085 // transitionTo requested. 1086 cancelAnimator(previousAnimator); 1087 } 1088 1089 if (mPendingFrameCallback != null) { 1090 // Display is off and we're waiting. 1091 return; 1092 } else if (mBlankScreen) { 1093 // Need to blank the display before continuing. 1094 blankDisplay(); 1095 return; 1096 } else if (!mScreenBlankingCallbackCalled) { 1097 // Not blanking the screen. Letting the callback know that we're ready 1098 // to replace what was on the screen before. 1099 if (mCallback != null) { 1100 mCallback.onDisplayBlanked(); 1101 mScreenBlankingCallbackCalled = true; 1102 } 1103 } 1104 1105 if (scrim == mScrimBehind) { 1106 dispatchBackScrimState(alpha); 1107 } 1108 1109 final boolean wantsAlphaUpdate = alpha != currentAlpha; 1110 final boolean wantsTintUpdate = scrim.getTint() != getCurrentScrimTint(scrim); 1111 1112 if (wantsAlphaUpdate || wantsTintUpdate) { 1113 if (mAnimateChange) { 1114 startScrimAnimation(scrim, currentAlpha); 1115 } else { 1116 // update the alpha directly 1117 updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); 1118 } 1119 } 1120 } 1121 cancelAnimator(ValueAnimator previousAnimator)1122 private void cancelAnimator(ValueAnimator previousAnimator) { 1123 if (previousAnimator != null) { 1124 previousAnimator.cancel(); 1125 } 1126 } 1127 blankDisplay()1128 private void blankDisplay() { 1129 updateScrimColor(mScrimInFront, 1, Color.BLACK); 1130 1131 // Notify callback that the screen is completely black and we're 1132 // ready to change the display power mode 1133 mPendingFrameCallback = () -> { 1134 if (mCallback != null) { 1135 mCallback.onDisplayBlanked(); 1136 mScreenBlankingCallbackCalled = true; 1137 } 1138 1139 mBlankingTransitionRunnable = () -> { 1140 mBlankingTransitionRunnable = null; 1141 mPendingFrameCallback = null; 1142 mBlankScreen = false; 1143 // Try again. 1144 updateScrims(); 1145 }; 1146 1147 // Setting power states can happen after we push out the frame. Make sure we 1148 // stay fully opaque until the power state request reaches the lower levels. 1149 final int delay = mScreenOn ? 32 : 500; 1150 if (DEBUG) { 1151 Log.d(TAG, "Fading out scrims with delay: " + delay); 1152 } 1153 mHandler.postDelayed(mBlankingTransitionRunnable, delay); 1154 }; 1155 doOnTheNextFrame(mPendingFrameCallback); 1156 } 1157 1158 /** 1159 * Executes a callback after the frame has hit the display. 1160 * 1161 * @param callback What to run. 1162 */ 1163 @VisibleForTesting doOnTheNextFrame(Runnable callback)1164 protected void doOnTheNextFrame(Runnable callback) { 1165 // Just calling View#postOnAnimation isn't enough because the frame might not have reached 1166 // the display yet. A timeout is the safest solution. 1167 mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */); 1168 } 1169 setScrimBehindChangeRunnable(Runnable changeRunnable)1170 public void setScrimBehindChangeRunnable(Runnable changeRunnable) { 1171 // TODO: remove this. This is necessary because of an order-of-operations limitation. 1172 // The fix is to move more of these class into @StatusBarScope 1173 if (mScrimBehind == null) { 1174 mScrimBehindChangeRunnable = changeRunnable; 1175 } else { 1176 mScrimBehind.setChangeRunnable(changeRunnable, mMainExecutor); 1177 } 1178 } 1179 setCurrentUser(int currentUser)1180 public void setCurrentUser(int currentUser) { 1181 // Don't care in the base class. 1182 } 1183 updateThemeColors()1184 private void updateThemeColors() { 1185 if (mScrimBehind == null) return; 1186 int background = Utils.getColorAttr(mScrimBehind.getContext(), 1187 android.R.attr.colorBackgroundFloating).getDefaultColor(); 1188 int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); 1189 mColors.setMainColor(background); 1190 mColors.setSecondaryColor(accent); 1191 mColors.setSupportsDarkText( 1192 ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); 1193 mNeedsDrawableColorUpdate = true; 1194 } 1195 onThemeChanged()1196 private void onThemeChanged() { 1197 updateThemeColors(); 1198 scheduleUpdate(); 1199 } 1200 1201 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1202 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1203 pw.println(" ScrimController: "); 1204 pw.print(" state: "); 1205 pw.println(mState); 1206 1207 pw.print(" frontScrim:"); 1208 pw.print(" viewAlpha="); 1209 pw.print(mScrimInFront.getViewAlpha()); 1210 pw.print(" alpha="); 1211 pw.print(mInFrontAlpha); 1212 pw.print(" tint=0x"); 1213 pw.println(Integer.toHexString(mScrimInFront.getTint())); 1214 1215 pw.print(" behindScrim:"); 1216 pw.print(" viewAlpha="); 1217 pw.print(mScrimBehind.getViewAlpha()); 1218 pw.print(" alpha="); 1219 pw.print(mBehindAlpha); 1220 pw.print(" tint=0x"); 1221 pw.println(Integer.toHexString(mScrimBehind.getTint())); 1222 1223 pw.print(" notificationsScrim:"); 1224 pw.print(" viewAlpha="); 1225 pw.print(mNotificationsScrim.getViewAlpha()); 1226 pw.print(" alpha="); 1227 pw.print(mNotificationsAlpha); 1228 pw.print(" tint=0x"); 1229 pw.println(Integer.toHexString(mNotificationsScrim.getTint())); 1230 1231 pw.print(" bubbleScrim:"); 1232 pw.print(" viewAlpha="); 1233 pw.print(mScrimForBubble.getViewAlpha()); 1234 pw.print(" alpha="); 1235 pw.print(mBubbleAlpha); 1236 pw.print(" tint=0x"); 1237 pw.println(Integer.toHexString(mScrimForBubble.getTint())); 1238 1239 pw.print(" mTracking="); 1240 pw.println(mTracking); 1241 pw.print(" mDefaultScrimAlpha="); 1242 pw.println(mDefaultScrimAlpha); 1243 pw.print(" mExpansionFraction="); 1244 pw.println(mPanelExpansion); 1245 1246 pw.print(" mState.getMaxLightRevealScrimAlpha="); 1247 pw.println(mState.getMaxLightRevealScrimAlpha()); 1248 } 1249 setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode)1250 public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { 1251 mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode; 1252 ScrimState[] states = ScrimState.values(); 1253 for (int i = 0; i < states.length; i++) { 1254 states[i].setWallpaperSupportsAmbientMode(wallpaperSupportsAmbientMode); 1255 } 1256 } 1257 1258 /** 1259 * Interrupts blanking transitions once the display notifies that it's already on. 1260 */ onScreenTurnedOn()1261 public void onScreenTurnedOn() { 1262 mScreenOn = true; 1263 if (mHandler.hasCallbacks(mBlankingTransitionRunnable)) { 1264 if (DEBUG) { 1265 Log.d(TAG, "Shorter blanking because screen turned on. All good."); 1266 } 1267 mHandler.removeCallbacks(mBlankingTransitionRunnable); 1268 mBlankingTransitionRunnable.run(); 1269 } 1270 } 1271 onScreenTurnedOff()1272 public void onScreenTurnedOff() { 1273 mScreenOn = false; 1274 } 1275 setExpansionAffectsAlpha(boolean expansionAffectsAlpha)1276 public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) { 1277 mExpansionAffectsAlpha = expansionAffectsAlpha; 1278 if (expansionAffectsAlpha) { 1279 applyAndDispatchState(); 1280 } 1281 } 1282 setKeyguardOccluded(boolean keyguardOccluded)1283 public void setKeyguardOccluded(boolean keyguardOccluded) { 1284 mKeyguardOccluded = keyguardOccluded; 1285 updateScrims(); 1286 } 1287 setHasBackdrop(boolean hasBackdrop)1288 public void setHasBackdrop(boolean hasBackdrop) { 1289 for (ScrimState state : ScrimState.values()) { 1290 state.setHasBackdrop(hasBackdrop); 1291 } 1292 1293 // Backdrop event may arrive after state was already applied, 1294 // in this case, back-scrim needs to be re-evaluated 1295 if (mState == ScrimState.AOD || mState == ScrimState.PULSING) { 1296 float newBehindAlpha = mState.getBehindAlpha(); 1297 if (isNaN(newBehindAlpha)) { 1298 throw new IllegalStateException("Scrim opacity is NaN for state: " + mState 1299 + ", back: " + mBehindAlpha); 1300 } 1301 if (mBehindAlpha != newBehindAlpha) { 1302 mBehindAlpha = newBehindAlpha; 1303 updateScrims(); 1304 } 1305 } 1306 } 1307 setKeyguardFadingAway(boolean fadingAway, long duration)1308 private void setKeyguardFadingAway(boolean fadingAway, long duration) { 1309 for (ScrimState state : ScrimState.values()) { 1310 state.setKeyguardFadingAway(fadingAway, duration); 1311 } 1312 } 1313 setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview)1314 public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) { 1315 for (ScrimState state : ScrimState.values()) { 1316 state.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); 1317 } 1318 } 1319 1320 public interface Callback { onStart()1321 default void onStart() { 1322 } 1323 onDisplayBlanked()1324 default void onDisplayBlanked() { 1325 } 1326 onFinished()1327 default void onFinished() { 1328 } 1329 onCancelled()1330 default void onCancelled() { 1331 } 1332 } 1333 1334 /** 1335 * Simple keyguard callback that updates scrims when keyguard visibility changes. 1336 */ 1337 private class KeyguardVisibilityCallback extends KeyguardUpdateMonitorCallback { 1338 1339 @Override onKeyguardVisibilityChanged(boolean showing)1340 public void onKeyguardVisibilityChanged(boolean showing) { 1341 mNeedsDrawableColorUpdate = true; 1342 scheduleUpdate(); 1343 } 1344 } 1345 } 1346