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 com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER; 20 import static com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB; 21 import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE; 22 import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN; 23 import static com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER; 24 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; 25 26 import static java.lang.Float.isNaN; 27 28 import android.animation.Animator; 29 import android.animation.AnimatorListenerAdapter; 30 import android.animation.ValueAnimator; 31 import android.annotation.IntDef; 32 import android.content.Context; 33 import android.graphics.Color; 34 import android.os.Handler; 35 import android.util.Log; 36 import android.util.MathUtils; 37 import android.util.Pair; 38 import android.view.View; 39 import android.view.ViewTreeObserver; 40 import android.view.animation.DecelerateInterpolator; 41 import android.view.animation.Interpolator; 42 43 import androidx.annotation.FloatRange; 44 import androidx.annotation.Nullable; 45 46 import com.android.app.tracing.coroutines.TrackTracer; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.colorextraction.ColorExtractor.GradientColors; 49 import com.android.internal.graphics.ColorUtils; 50 import com.android.internal.util.ContrastColorUtil; 51 import com.android.internal.util.function.TriConsumer; 52 import com.android.keyguard.BouncerPanelExpansionCalculator; 53 import com.android.keyguard.KeyguardUpdateMonitor; 54 import com.android.keyguard.KeyguardUpdateMonitorCallback; 55 import com.android.systemui.Dumpable; 56 import com.android.systemui.Flags; 57 import com.android.systemui.animation.ShadeInterpolation; 58 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; 59 import com.android.systemui.dagger.SysUISingleton; 60 import com.android.systemui.dagger.qualifiers.Main; 61 import com.android.systemui.dock.DockManager; 62 import com.android.systemui.keyguard.KeyguardUnlockAnimationController; 63 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; 64 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; 65 import com.android.systemui.keyguard.shared.model.Edge; 66 import com.android.systemui.keyguard.shared.model.KeyguardState; 67 import com.android.systemui.keyguard.shared.model.ScrimAlpha; 68 import com.android.systemui.keyguard.shared.model.TransitionState; 69 import com.android.systemui.keyguard.shared.model.TransitionStep; 70 import com.android.systemui.keyguard.ui.transitions.BlurConfig; 71 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel; 72 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; 73 import com.android.systemui.res.R; 74 import com.android.systemui.scene.shared.flag.SceneContainerFlag; 75 import com.android.systemui.scene.shared.model.Scenes; 76 import com.android.systemui.scrim.ScrimView; 77 import com.android.systemui.shade.ShadeViewController; 78 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; 79 import com.android.systemui.statusbar.notification.stack.ViewState; 80 import com.android.systemui.statusbar.policy.ConfigurationController; 81 import com.android.systemui.statusbar.policy.KeyguardStateController; 82 import com.android.systemui.util.kotlin.JavaAdapter; 83 import com.android.systemui.util.wakelock.DelayedWakeLock; 84 import com.android.systemui.util.wakelock.WakeLock; 85 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor; 86 87 import dagger.Lazy; 88 89 import kotlinx.coroutines.CoroutineDispatcher; 90 91 import java.io.PrintWriter; 92 import java.lang.annotation.Retention; 93 import java.lang.annotation.RetentionPolicy; 94 import java.util.concurrent.Executor; 95 import java.util.function.Consumer; 96 import java.util.function.Supplier; 97 98 import javax.inject.Inject; 99 100 /** 101 * Controls both the scrim behind the notifications and in front of the notifications (when a 102 * security method gets shown). 103 */ 104 @SysUISingleton 105 public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable { 106 107 static final String TAG = "ScrimController"; 108 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 109 110 // debug mode colors scrims with below debug colors, irrespectively of which state they're in 111 public static final boolean DEBUG_MODE = false; 112 113 public static final int DEBUG_NOTIFICATIONS_TINT = Color.RED; 114 public static final int DEBUG_FRONT_TINT = Color.GREEN; 115 public static final int DEBUG_BEHIND_TINT = Color.BLUE; 116 117 /** 118 * General scrim animation duration. 119 */ 120 public static final long ANIMATION_DURATION = 220; 121 /** 122 * Longer duration, currently only used when going to AOD. 123 */ 124 public static final long ANIMATION_DURATION_LONG = 1000; 125 /** 126 * When both scrims have 0 alpha. 127 */ 128 public static final int TRANSPARENT = 0; 129 /** 130 * When scrims aren't transparent (alpha 0) but also not opaque (alpha 1.) 131 */ 132 public static final int SEMI_TRANSPARENT = 1; 133 /** 134 * When at least 1 scrim is fully opaque (alpha set to 1.) 135 */ 136 public static final int OPAQUE = 2; 137 private boolean mClipsQsScrim; 138 139 /** 140 * Whether an activity is launching over the lockscreen. During the launch animation, we want to 141 * delay certain scrim changes until after the animation ends. 142 */ 143 private boolean mOccludeAnimationPlaying = false; 144 145 /** 146 * The amount of progress we are currently in if we're transitioning to the full shade. 147 * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full 148 * shade. 149 */ 150 private float mTransitionToFullShadeProgress; 151 152 /** 153 * Same as {@link #mTransitionToFullShadeProgress}, but specifically for the notifications scrim 154 * on the lock screen. 155 * 156 * On split shade lock screen we want the different scrims to fade in at different times and 157 * rates. 158 */ 159 private float mTransitionToLockScreenFullShadeNotificationsProgress; 160 161 /** 162 * If we're currently transitioning to the full shade. 163 */ 164 private boolean mTransitioningToFullShade; 165 166 /** 167 * The percentage of the bouncer which is hidden. If 1, the bouncer is completely hidden. If 168 * 0, the bouncer is visible. 169 */ 170 @FloatRange(from = 0, to = 1) 171 private float mBouncerHiddenFraction = KeyguardBouncerConstants.EXPANSION_HIDDEN; 172 private boolean mIsBlurSupported = false; 173 getDefaultScrimAlpha(boolean ignoreCurrentState)174 private float getDefaultScrimAlpha(boolean ignoreCurrentState) { 175 if (Flags.bouncerUiRevamp() && mIsBlurSupported) { 176 // Hack to not make the shade transparent when shade blur is not enabled. 177 if (!Flags.notificationShadeBlur() && !ignoreCurrentState) { 178 // When we expand directly to full quick settings, shade state is KEYGUARD 179 if (mState == ScrimState.SHADE_LOCKED || (mState == ScrimState.KEYGUARD 180 && mQsExpansion == 1)) { 181 return BUSY_SCRIM_ALPHA; 182 } 183 } 184 return TRANSPARENT_BOUNCER_SCRIM_ALPHA; 185 } else { 186 return BUSY_SCRIM_ALPHA; 187 } 188 } 189 getDefaultScrimAlpha()190 private float getDefaultScrimAlpha() { 191 return getDefaultScrimAlpha(false); 192 } 193 194 @IntDef(prefix = {"VISIBILITY_"}, value = { 195 TRANSPARENT, 196 SEMI_TRANSPARENT, 197 OPAQUE 198 }) 199 @Retention(RetentionPolicy.SOURCE) 200 public @interface ScrimVisibility { 201 } 202 203 /** 204 * Default alpha value for most scrims. 205 */ 206 protected static final float KEYGUARD_SCRIM_ALPHA = 0.2f; 207 /** 208 * Scrim opacity when the phone is about to wake-up. 209 */ 210 public static final float WAKE_SENSOR_SCRIM_ALPHA = 0.6f; 211 212 /** 213 * The default scrim under the shade and dialogs. 214 * This should not be lower than 0.54, otherwise we won't pass GAR. 215 */ 216 public static final float BUSY_SCRIM_ALPHA = 1f; 217 218 /** 219 * Scrim opacity that can have text on top. 220 */ 221 public static final float GAR_SCRIM_ALPHA = 0.6f; 222 223 static final int TAG_KEY_ANIM = R.id.scrim; 224 private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; 225 private static final int TAG_END_ALPHA = R.id.scrim_alpha_end; 226 private static final float NOT_INITIALIZED = -1; 227 228 private ScrimState mState = ScrimState.UNINITIALIZED; 229 230 private Context mContext; 231 232 private ScrimView mScrimInFront; 233 private ScrimView mNotificationsScrim; 234 private ScrimView mScrimBehind; 235 236 private final KeyguardStateController mKeyguardStateController; 237 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 238 private final DozeParameters mDozeParameters; 239 private final DockManager mDockManager; 240 private final KeyguardVisibilityCallback mKeyguardVisibilityCallback; 241 private final Handler mHandler; 242 private final Executor mMainExecutor; 243 private final JavaAdapter mJavaAdapter; 244 private final ScreenOffAnimationController mScreenOffAnimationController; 245 private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; 246 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 247 private final KeyguardInteractor mKeyguardInteractor; 248 249 private GradientColors mColors; 250 private boolean mNeedsDrawableColorUpdate; 251 252 private float mAdditionalScrimBehindAlphaKeyguard = 0f; 253 // Combined scrim behind keyguard alpha of default scrim + additional scrim 254 private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA; 255 256 static final float TRANSPARENT_BOUNCER_SCRIM_ALPHA = 0.54f; 257 258 private float mRawPanelExpansionFraction; 259 private float mPanelScrimMinFraction; 260 // Calculated based on mRawPanelExpansionFraction and mPanelScrimMinFraction 261 private float mPanelExpansionFraction = 1f; // Assume shade is expanded during initialization 262 private float mQsExpansion; 263 private boolean mQsBottomVisible; 264 private boolean mAnimatingPanelExpansionOnUnlock; // don't animate scrim 265 266 private boolean mDarkenWhileDragging; 267 private boolean mExpansionAffectsAlpha = true; 268 private boolean mAnimateChange; 269 private boolean mUpdatePending; 270 private long mAnimationDuration = -1; 271 private long mAnimationDelay; 272 private Animator.AnimatorListener mAnimatorListener; 273 private final Interpolator mInterpolator = new DecelerateInterpolator(); 274 275 private float mInFrontAlpha = NOT_INITIALIZED; 276 private float mBehindAlpha = NOT_INITIALIZED; 277 private float mNotificationsAlpha = NOT_INITIALIZED; 278 279 private int mInFrontTint; 280 private int mBehindTint; 281 private int mNotificationsTint; 282 283 private int mScrimsVisibility; 284 private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener; 285 private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; 286 private final BlurConfig mBlurConfig; 287 private final Lazy<WindowRootViewBlurInteractor> mWindowRootViewBlurInteractor; 288 private Consumer<Integer> mScrimVisibleListener; 289 private boolean mBlankScreen; 290 private boolean mScreenBlankingCallbackCalled; 291 private Callback mCallback; 292 private boolean mScreenOn; 293 private boolean mTransparentScrimBackground; 294 295 // Scrim blanking callbacks 296 private Runnable mPendingFrameCallback; 297 private Runnable mBlankingTransitionRunnable; 298 299 private final WakeLock mWakeLock; 300 private boolean mWakeLockHeld; 301 private boolean mKeyguardOccluded; 302 303 private KeyguardTransitionInteractor mKeyguardTransitionInteractor; 304 private CoroutineDispatcher mMainDispatcher; 305 private boolean mIsBouncerToGoneTransitionRunning = false; 306 private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; 307 private AlternateBouncerToGoneTransitionViewModel mAlternateBouncerToGoneTransitionViewModel; 308 private final Consumer<ScrimAlpha> mScrimAlphaConsumer = 309 (ScrimAlpha alphas) -> { 310 mInFrontAlpha = alphas.getFrontAlpha(); 311 mScrimInFront.setViewAlpha(mInFrontAlpha); 312 313 mNotificationsAlpha = alphas.getNotificationsAlpha(); 314 mNotificationsScrim.setViewAlpha(mNotificationsAlpha); 315 316 mBehindAlpha = alphas.getBehindAlpha(); 317 mScrimBehind.setViewAlpha(mBehindAlpha); 318 }; 319 320 /** 321 * Consumer that fades the behind scrim in and out during the transition between the lock screen 322 * and the glanceable hub. 323 * 324 * While the lock screen is showing, the behind scrim is used to slightly darken the lock screen 325 * wallpaper underneath. Since the glanceable hub is under all of the scrims, we want to fade 326 * out the scrim so that the glanceable hub isn't darkened when it opens. 327 * 328 * {@link #applyState()} handles the scrim alphas once on the glanceable hub, this is only 329 * responsible for setting the behind alpha during the transition. 330 */ 331 private final Consumer<TransitionStep> mGlanceableHubConsumer = (TransitionStep step) -> { 332 final float baseAlpha = ScrimState.KEYGUARD.getBehindAlpha(); 333 final float transitionProgress = step.getValue(); 334 if (step.getTo() == KeyguardState.LOCKSCREEN) { 335 // Transitioning back to lock screen, fade in behind scrim again. 336 mBehindAlpha = baseAlpha * transitionProgress; 337 } else if (step.getTo() == GLANCEABLE_HUB) { 338 // Transitioning to glanceable hub, fade out behind scrim. 339 mBehindAlpha = baseAlpha * (1 - transitionProgress); 340 } 341 mScrimBehind.setViewAlpha(mBehindAlpha); 342 }; 343 344 @VisibleForTesting 345 Consumer<TransitionStep> mBouncerToGoneTransition; 346 347 private boolean mViewsAttached; 348 349 @Inject ScrimController( LightBarController lightBarController, DozeParameters dozeParameters, KeyguardStateController keyguardStateController, DelayedWakeLock.Factory delayedWakeLockFactory, @Main Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, ConfigurationController configurationController, @Main Executor mainExecutor, JavaAdapter javaAdapter, ScreenOffAnimationController screenOffAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, AlternateBouncerToGoneTransitionViewModel alternateBouncerToGoneTransitionViewModel, KeyguardTransitionInteractor keyguardTransitionInteractor, KeyguardInteractor keyguardInteractor, @Main CoroutineDispatcher mainDispatcher, LargeScreenShadeInterpolator largeScreenShadeInterpolator, BlurConfig blurConfig, @Main Context context, Lazy<WindowRootViewBlurInteractor> windowRootViewBlurInteractor)350 public ScrimController( 351 LightBarController lightBarController, 352 DozeParameters dozeParameters, 353 KeyguardStateController keyguardStateController, 354 DelayedWakeLock.Factory delayedWakeLockFactory, 355 @Main Handler handler, 356 KeyguardUpdateMonitor keyguardUpdateMonitor, 357 DockManager dockManager, 358 ConfigurationController configurationController, 359 @Main Executor mainExecutor, 360 JavaAdapter javaAdapter, 361 ScreenOffAnimationController screenOffAnimationController, 362 KeyguardUnlockAnimationController keyguardUnlockAnimationController, 363 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 364 PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, 365 AlternateBouncerToGoneTransitionViewModel alternateBouncerToGoneTransitionViewModel, 366 KeyguardTransitionInteractor keyguardTransitionInteractor, 367 KeyguardInteractor keyguardInteractor, 368 @Main CoroutineDispatcher mainDispatcher, 369 LargeScreenShadeInterpolator largeScreenShadeInterpolator, 370 BlurConfig blurConfig, 371 @Main Context context, 372 Lazy<WindowRootViewBlurInteractor> windowRootViewBlurInteractor) { 373 mContext = context; 374 mScrimStateListener = lightBarController::setScrimState; 375 mLargeScreenShadeInterpolator = largeScreenShadeInterpolator; 376 mBlurConfig = blurConfig; 377 mWindowRootViewBlurInteractor = windowRootViewBlurInteractor; 378 379 mKeyguardStateController = keyguardStateController; 380 mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); 381 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 382 mKeyguardVisibilityCallback = new KeyguardVisibilityCallback(); 383 mHandler = handler; 384 mMainExecutor = mainExecutor; 385 mJavaAdapter = javaAdapter; 386 mScreenOffAnimationController = screenOffAnimationController; 387 mWakeLock = delayedWakeLockFactory.create("Scrims"); 388 // Scrim alpha is initially set to the value on the resource but might be changed 389 // to make sure that text on top of it is legible. 390 mDozeParameters = dozeParameters; 391 mDockManager = dockManager; 392 mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; 393 keyguardStateController.addCallback(new KeyguardStateController.Callback() { 394 @Override 395 public void onKeyguardFadingAwayChanged() { 396 setKeyguardFadingAway(keyguardStateController.isKeyguardFadingAway(), 397 keyguardStateController.getKeyguardFadingAwayDuration()); 398 } 399 }); 400 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 401 configurationController.addCallback(new ConfigurationController.ConfigurationListener() { 402 @Override 403 public void onThemeChanged() { 404 ScrimController.this.onThemeChanged(); 405 } 406 407 @Override 408 public void onUiModeChanged() { 409 ScrimController.this.onThemeChanged(); 410 } 411 }); 412 mColors = new GradientColors(); 413 mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel; 414 mAlternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel; 415 mKeyguardTransitionInteractor = keyguardTransitionInteractor; 416 mKeyguardInteractor = keyguardInteractor; 417 mMainDispatcher = mainDispatcher; 418 } 419 420 /** 421 * Attach the controller to the supplied views. 422 */ attachViews(ScrimView behindScrim, ScrimView notificationsScrim, ScrimView scrimInFront)423 public void attachViews(ScrimView behindScrim, ScrimView notificationsScrim, 424 ScrimView scrimInFront) { 425 mNotificationsScrim = notificationsScrim; 426 mScrimBehind = behindScrim; 427 mScrimInFront = scrimInFront; 428 updateThemeColors(); 429 mNotificationsScrim.setScrimName(getScrimName(mNotificationsScrim)); 430 mScrimBehind.setScrimName(getScrimName(mScrimBehind)); 431 mScrimInFront.setScrimName(getScrimName(mScrimInFront)); 432 433 behindScrim.enableBottomEdgeConcave(mClipsQsScrim); 434 mNotificationsScrim.enableRoundedCorners(true); 435 436 final ScrimState[] states = ScrimState.values(); 437 for (int i = 0; i < states.length; i++) { 438 states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager, 439 this::isBlurCurrentlySupported); 440 states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard); 441 states[i].setDefaultScrimAlpha(getDefaultScrimAlpha()); 442 } 443 444 mTransparentScrimBackground = notificationsScrim.getResources() 445 .getBoolean(R.bool.notification_scrim_transparent); 446 updateScrims(); 447 mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); 448 449 // prepare() sets proper initial values for most states 450 for (ScrimState state : ScrimState.values()) { 451 state.prepare(state); 452 } 453 454 hydrateStateInternally(behindScrim); 455 456 mViewsAttached = true; 457 } 458 hydrateStateInternally(ScrimView behindScrim)459 private void hydrateStateInternally(ScrimView behindScrim) { 460 if (SceneContainerFlag.isEnabled()) { 461 return; 462 } 463 464 // Directly control transition to UNLOCKED scrim state from PRIMARY_BOUNCER, and make sure 465 // to report back that keyguard has faded away. This fixes cases where the scrim state was 466 // rapidly switching on unlock, due to shifts in state in CentralSurfacesImpl 467 mBouncerToGoneTransition = 468 (TransitionStep step) -> { 469 TransitionState state = step.getTransitionState(); 470 471 mIsBouncerToGoneTransitionRunning = state == TransitionState.RUNNING; 472 473 if (state == TransitionState.STARTED) { 474 setExpansionAffectsAlpha(false); 475 legacyTransitionTo(ScrimState.UNLOCKED); 476 } 477 478 if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) { 479 setExpansionAffectsAlpha(true); 480 if (mKeyguardStateController.isKeyguardFadingAway()) { 481 mStatusBarKeyguardViewManager.onKeyguardFadedAway(); 482 } 483 dispatchScrimsVisible(); 484 dispatchBackScrimState(mScrimBehind.getViewAlpha()); 485 } 486 }; 487 488 // PRIMARY_BOUNCER->GONE 489 collectFlow(behindScrim, mKeyguardTransitionInteractor.transition( 490 Edge.Companion.getINVALID(), 491 Edge.Companion.create(PRIMARY_BOUNCER, GONE)), 492 mBouncerToGoneTransition, mMainDispatcher); 493 collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(), 494 mScrimAlphaConsumer, mMainDispatcher); 495 496 // ALTERNATE_BOUNCER->GONE 497 collectFlow(behindScrim, mKeyguardTransitionInteractor.transition( 498 Edge.Companion.create(ALTERNATE_BOUNCER, Scenes.Gone), 499 Edge.Companion.create(ALTERNATE_BOUNCER, GONE)), 500 mBouncerToGoneTransition, mMainDispatcher); 501 collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(), 502 mScrimAlphaConsumer, mMainDispatcher); 503 504 // LOCKSCREEN<->GLANCEABLE_HUB 505 collectFlow( 506 behindScrim, 507 mKeyguardTransitionInteractor.transition( 508 Edge.Companion.create(LOCKSCREEN, Scenes.Communal), 509 Edge.Companion.create(LOCKSCREEN, GLANCEABLE_HUB)), 510 mGlanceableHubConsumer, 511 mMainDispatcher); 512 collectFlow(behindScrim, 513 mKeyguardTransitionInteractor.transition( 514 Edge.Companion.create(Scenes.Communal, LOCKSCREEN), 515 Edge.Companion.create(GLANCEABLE_HUB, LOCKSCREEN)), 516 mGlanceableHubConsumer, mMainDispatcher); 517 518 if (Flags.bouncerUiRevamp() || Flags.notificationShadeBlur()) { 519 collectFlow(behindScrim, 520 mWindowRootViewBlurInteractor.get().isBlurCurrentlySupported(), 521 this::handleBlurSupportedChanged); 522 } 523 } 524 updateDefaultScrimAlphas()525 private void updateDefaultScrimAlphas() { 526 for (ScrimState state : ScrimState.values()) { 527 state.setDefaultScrimAlpha(getDefaultScrimAlpha(true)); 528 } 529 applyAndDispatchState(); 530 } 531 isBlurCurrentlySupported()532 private boolean isBlurCurrentlySupported() { 533 return mWindowRootViewBlurInteractor.get() 534 .isBlurCurrentlySupported() 535 .getValue(); 536 } 537 handleBlurSupportedChanged(boolean isBlurSupported)538 private void handleBlurSupportedChanged(boolean isBlurSupported) { 539 this.mIsBlurSupported = isBlurSupported; 540 if (Flags.bouncerUiRevamp()) { 541 updateDefaultScrimAlphas(); 542 if (isBlurSupported) { 543 ScrimState.BOUNCER_SCRIMMED.setNotifBlurRadius(mBlurConfig.getMaxBlurRadiusPx()); 544 } else { 545 ScrimState.BOUNCER_SCRIMMED.setNotifBlurRadius(0f); 546 } 547 } 548 if (Flags.notificationShadeBlur()) { 549 mState.prepare(mState); 550 applyAndDispatchState(); 551 } 552 } 553 554 // TODO(b/270984686) recompute scrim height accurately, based on shade contents. 555 /** Set corner radius of the bottom edge of the Notification scrim. */ setNotificationBottomRadius(float radius)556 public void setNotificationBottomRadius(float radius) { 557 if (mNotificationsScrim == null) { 558 return; 559 } 560 mNotificationsScrim.setBottomEdgeRadius(radius); 561 } 562 563 /** Sets corner radius of scrims. */ setScrimCornerRadius(int radius)564 public void setScrimCornerRadius(int radius) { 565 if (mScrimBehind == null || mNotificationsScrim == null) { 566 return; 567 } 568 mScrimBehind.setCornerRadius(radius); 569 mNotificationsScrim.setCornerRadius(radius); 570 } 571 setScrimVisibleListener(Consumer<Integer> listener)572 void setScrimVisibleListener(Consumer<Integer> listener) { 573 mScrimVisibleListener = listener; 574 } 575 transitionTo(ScrimState state)576 public void transitionTo(ScrimState state) { 577 if (SceneContainerFlag.isUnexpectedlyInLegacyMode() || !mViewsAttached) { 578 return; 579 } 580 581 internalTransitionTo(state, null); 582 } 583 584 /** 585 * Transitions to the given {@link ScrimState}. 586 * 587 * @deprecated Legacy codepath only. Do not call directly. 588 */ 589 @Deprecated legacyTransitionTo(ScrimState state)590 public void legacyTransitionTo(ScrimState state) { 591 SceneContainerFlag.assertInLegacyMode(); 592 internalTransitionTo(state, null); 593 } 594 595 /** 596 * Transitions to the given {@link ScrimState}. 597 * 598 * @deprecated Legacy codepath only. Do not call directly. 599 */ 600 @Deprecated legacyTransitionTo(ScrimState state, Callback callback)601 public void legacyTransitionTo(ScrimState state, Callback callback) { 602 SceneContainerFlag.assertInLegacyMode(); 603 internalTransitionTo(state, callback); 604 } 605 internalTransitionTo(ScrimState state, Callback callback)606 private void internalTransitionTo(ScrimState state, Callback callback) { 607 if (mIsBouncerToGoneTransitionRunning) { 608 Log.i(TAG, "Skipping transition to: " + state 609 + " while mIsBouncerToGoneTransitionRunning"); 610 return; 611 } 612 if (state == mState) { 613 // Call the callback anyway, unless it's already enqueued 614 if (callback != null && mCallback != callback) { 615 callback.onFinished(); 616 } 617 return; 618 } else if (DEBUG) { 619 Log.d(TAG, "State changed to: " + state); 620 } 621 622 if (state == ScrimState.UNINITIALIZED) { 623 throw new IllegalArgumentException("Cannot change to UNINITIALIZED."); 624 } 625 626 final ScrimState oldState = mState; 627 mState = state; 628 TrackTracer.instantForGroup("scrim", "state", mState.ordinal()); 629 630 if (mCallback != null) { 631 mCallback.onCancelled(); 632 } 633 mCallback = callback; 634 635 state.prepare(oldState); 636 mScreenBlankingCallbackCalled = false; 637 mAnimationDelay = 0; 638 mBlankScreen = state.getBlanksScreen(); 639 mAnimateChange = state.getAnimateChange(); 640 mAnimationDuration = state.getAnimationDuration(); 641 642 if (mState == ScrimState.GLANCEABLE_HUB_OVER_DREAM) { 643 // When the device is docked while on GLANCEABLE_HUB, the dream starts underneath the 644 // hub and the ScrimState transitions to GLANCEABLE_HUB_OVER_DREAM. To prevent the 645 // scrims from flickering in during this transition, we set the panel expansion 646 // fraction, which is 1 when idle on GLANCEABLE_HUB, to 0. This only occurs when the hub 647 // is open because the hub lives in the same window as the shade, which is not visible 648 // when transitioning from KEYGUARD to DREAMING. 649 mPanelExpansionFraction = 0f; 650 } 651 652 applyState(); 653 654 mScrimInFront.setBlendWithMainColor(state.shouldBlendWithMainColor()); 655 656 // Cancel blanking transitions that were pending before we requested a new state 657 if (mPendingFrameCallback != null) { 658 mScrimBehind.removeCallbacks(mPendingFrameCallback); 659 mPendingFrameCallback = null; 660 } 661 if (mHandler.hasCallbacks(mBlankingTransitionRunnable)) { 662 mHandler.removeCallbacks(mBlankingTransitionRunnable); 663 mBlankingTransitionRunnable = null; 664 } 665 666 // Showing/hiding the keyguard means that scrim colors have to be switched, not necessary 667 // to do the same when you're just showing the brightness mirror. 668 mNeedsDrawableColorUpdate = state != ScrimState.BRIGHTNESS_MIRROR; 669 670 // The device might sleep if it's entering AOD, we need to make sure that 671 // the animation plays properly until the last frame. 672 // It's important to avoid holding the wakelock unless necessary because 673 // WakeLock#aqcuire will trigger an IPC and will cause jank. 674 if (mState.isLowPowerState()) { 675 holdWakeLock(); 676 } 677 678 if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) { 679 mAnimationDelay = CentralSurfaces.FADE_KEYGUARD_START_DELAY; 680 scheduleUpdate(); 681 } else if (((oldState == ScrimState.AOD || oldState == ScrimState.PULSING) // leaving doze 682 && (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED)) 683 || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) { 684 // Scheduling a frame isn't enough when: 685 // • Leaving doze and we need to modify scrim color immediately 686 // • ColorFade will not kick-in and scrim cannot wait for pre-draw. 687 onPreDraw(); 688 } else { 689 // Schedule a frame 690 scheduleUpdate(); 691 } 692 693 dispatchBackScrimState(mScrimBehind.getViewAlpha()); 694 } 695 getState()696 public ScrimState getState() { 697 return mState; 698 } 699 700 /** 701 * Sets the additional scrim behind alpha keyguard that would be blended with the default scrim 702 * by applying alpha composition on both values. 703 * 704 * @param additionalScrimAlpha alpha value of additional scrim behind alpha keyguard. 705 */ setAdditionalScrimBehindAlphaKeyguard(float additionalScrimAlpha)706 protected void setAdditionalScrimBehindAlphaKeyguard(float additionalScrimAlpha) { 707 mAdditionalScrimBehindAlphaKeyguard = additionalScrimAlpha; 708 } 709 710 /** 711 * Applies alpha composition to the default scrim behind alpha keyguard and the additional 712 * scrim alpha, and sets this value to the scrim behind alpha keyguard. 713 * This is used to apply additional keyguard dimming on top of the default scrim alpha value. 714 */ applyCompositeAlphaOnScrimBehindKeyguard()715 protected void applyCompositeAlphaOnScrimBehindKeyguard() { 716 int compositeAlpha = ColorUtils.compositeAlpha( 717 (int) (255 * mAdditionalScrimBehindAlphaKeyguard), 718 (int) (255 * KEYGUARD_SCRIM_ALPHA)); 719 float keyguardScrimAlpha = (float) compositeAlpha / 255; 720 setScrimBehindValues(keyguardScrimAlpha); 721 } 722 723 /** 724 * Sets the scrim behind alpha keyguard values. This is how much the keyguard will be dimmed. 725 * 726 * @param scrimBehindAlphaKeyguard alpha value of the scrim behind 727 */ setScrimBehindValues(float scrimBehindAlphaKeyguard)728 private void setScrimBehindValues(float scrimBehindAlphaKeyguard) { 729 mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard; 730 ScrimState[] states = ScrimState.values(); 731 for (int i = 0; i < states.length; i++) { 732 states[i].setScrimBehindAlphaKeyguard(scrimBehindAlphaKeyguard); 733 } 734 scheduleUpdate(); 735 } 736 737 /** This is used by the predictive back gesture animation to scale the Shade. */ applyBackScaling(float scale)738 public void applyBackScaling(float scale) { 739 mNotificationsScrim.setScaleX(scale); 740 mNotificationsScrim.setScaleY(scale); 741 } 742 getBackScaling()743 public float getBackScaling() { 744 return mNotificationsScrim.getScaleY(); 745 } 746 onTrackingStarted()747 public void onTrackingStarted() { 748 mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); 749 if (!mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { 750 mAnimatingPanelExpansionOnUnlock = false; 751 } 752 } 753 holdWakeLock()754 private void holdWakeLock() { 755 if (!mWakeLockHeld) { 756 if (mWakeLock != null) { 757 mWakeLockHeld = true; 758 mWakeLock.acquire(TAG); 759 } else { 760 Log.w(TAG, "Cannot hold wake lock, it has not been set yet"); 761 } 762 } 763 } 764 765 /** 766 * Current state of the shade expansion when pulling it from the top. 767 * This value is 1 when on top of the keyguard and goes to 0 as the user drags up. 768 * 769 * The expansion fraction is tied to the scrim opacity. 770 * 771 * See {@link ScrimShadeTransitionController#onPanelExpansionChanged}. 772 * 773 * @param rawPanelExpansionFraction From 0 to 1 where 0 means collapsed and 1 expanded. 774 */ setRawPanelExpansionFraction( @loatRangefrom = 0.0, to = 1.0) float rawPanelExpansionFraction)775 public void setRawPanelExpansionFraction( 776 @FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) { 777 if (isNaN(rawPanelExpansionFraction)) { 778 throw new IllegalArgumentException("rawPanelExpansionFraction should not be NaN"); 779 } 780 mRawPanelExpansionFraction = rawPanelExpansionFraction; 781 calculateAndUpdatePanelExpansion(); 782 } 783 784 /** See {@link ShadeViewController#setPanelScrimMinFraction(float)}. */ setPanelScrimMinFraction(float minFraction)785 public void setPanelScrimMinFraction(float minFraction) { 786 if (isNaN(minFraction)) { 787 throw new IllegalArgumentException("minFraction should not be NaN"); 788 } 789 mPanelScrimMinFraction = minFraction; 790 calculateAndUpdatePanelExpansion(); 791 } 792 calculateAndUpdatePanelExpansion()793 private void calculateAndUpdatePanelExpansion() { 794 float panelExpansionFraction = mRawPanelExpansionFraction; 795 if (mPanelScrimMinFraction < 1.0f) { 796 panelExpansionFraction = Math.max( 797 (mRawPanelExpansionFraction - mPanelScrimMinFraction) 798 / (1.0f - mPanelScrimMinFraction), 799 0); 800 } 801 802 if (mPanelExpansionFraction != panelExpansionFraction) { 803 if (panelExpansionFraction != 0f 804 && mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation() 805 && mState != ScrimState.UNLOCKED) { 806 mAnimatingPanelExpansionOnUnlock = true; 807 } else if (panelExpansionFraction == 0f) { 808 mAnimatingPanelExpansionOnUnlock = false; 809 } 810 811 mPanelExpansionFraction = panelExpansionFraction; 812 813 boolean relevantState = (mState == ScrimState.UNLOCKED 814 || mState == ScrimState.KEYGUARD 815 || mState == ScrimState.DREAMING 816 || mState == ScrimState.GLANCEABLE_HUB_OVER_DREAM 817 || mState == ScrimState.SHADE_LOCKED 818 || mState == ScrimState.PULSING); 819 if (!(relevantState && mExpansionAffectsAlpha) || mAnimatingPanelExpansionOnUnlock) { 820 return; 821 } 822 applyAndDispatchState(); 823 } 824 } 825 onUnlockAnimationFinished()826 public void onUnlockAnimationFinished() { 827 mAnimatingPanelExpansionOnUnlock = false; 828 applyAndDispatchState(); 829 } 830 831 /** 832 * Set the amount of progress we are currently in if we're transitioning to the full shade. 833 * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full 834 * shade. 835 * 836 * @param progress the progress for all scrims. 837 * @param lockScreenNotificationsProgress the progress specifically for the notifications scrim. 838 */ setTransitionToFullShadeProgress(float progress, float lockScreenNotificationsProgress)839 public void setTransitionToFullShadeProgress(float progress, 840 float lockScreenNotificationsProgress) { 841 if (progress != mTransitionToFullShadeProgress || lockScreenNotificationsProgress 842 != mTransitionToLockScreenFullShadeNotificationsProgress) { 843 mTransitionToFullShadeProgress = progress; 844 mTransitionToLockScreenFullShadeNotificationsProgress = lockScreenNotificationsProgress; 845 setTransitionToFullShade(progress > 0.0f || lockScreenNotificationsProgress > 0.0f); 846 applyAndDispatchState(); 847 } 848 } 849 850 /** 851 * Set if we're currently transitioning to the full shade 852 */ setTransitionToFullShade(boolean transitioning)853 private void setTransitionToFullShade(boolean transitioning) { 854 if (transitioning != mTransitioningToFullShade) { 855 mTransitioningToFullShade = transitioning; 856 } 857 } 858 859 860 /** 861 * Set bounds for notifications background, all coordinates are absolute 862 */ setNotificationsBounds(float left, float top, float right, float bottom)863 public void setNotificationsBounds(float left, float top, float right, float bottom) { 864 if (mClipsQsScrim) { 865 // notification scrim's rounded corners are anti-aliased, but clipping of the QS/behind 866 // scrim can't be and it's causing jagged corners. That's why notification scrim needs 867 // to overlap QS scrim by one pixel horizontally (left - 1 and right + 1) 868 // see: b/186644628 869 mNotificationsScrim.setDrawableBounds(left - 1, top, right + 1, bottom); 870 mScrimBehind.setBottomEdgePosition((int) top); 871 } else { 872 mNotificationsScrim.setDrawableBounds(left, top, right, bottom); 873 } 874 875 // Only clip if the notif scrim is visible 876 if (mNotificationsAlpha > 0f) { 877 mKeyguardInteractor.setTopClippingBounds((int) top); 878 } else { 879 mKeyguardInteractor.setTopClippingBounds(null); 880 } 881 } 882 883 /** 884 * Sets the amount of vertical over scroll that should be performed on the notifications scrim. 885 */ setNotificationsOverScrollAmount(int overScrollAmount)886 public void setNotificationsOverScrollAmount(int overScrollAmount) { 887 if (mNotificationsScrim != null) mNotificationsScrim.setTranslationY(overScrollAmount); 888 } 889 890 /** 891 * Current state of the QuickSettings when pulling it from the top. 892 * 893 * @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded. 894 * @param qsPanelBottomY Absolute Y position of qs panel bottom 895 */ setQsPosition(float expansionFraction, int qsPanelBottomY)896 public void setQsPosition(float expansionFraction, int qsPanelBottomY) { 897 if (isNaN(expansionFraction)) { 898 return; 899 } 900 expansionFraction = ShadeInterpolation.getNotificationScrimAlpha(expansionFraction); 901 boolean qsBottomVisible = qsPanelBottomY > 0; 902 if (mQsExpansion != expansionFraction || mQsBottomVisible != qsBottomVisible) { 903 mQsExpansion = expansionFraction; 904 mQsBottomVisible = qsBottomVisible; 905 boolean relevantState = (mState == ScrimState.SHADE_LOCKED 906 || mState == ScrimState.KEYGUARD 907 || mState == ScrimState.PULSING); 908 if (!(relevantState && mExpansionAffectsAlpha)) { 909 return; 910 } 911 applyAndDispatchState(); 912 } 913 } 914 915 /** 916 * Updates the percentage of the bouncer which is hidden. 917 */ setBouncerHiddenFraction(@loatRangefrom = 0, to = 1) float bouncerHiddenAmount)918 public void setBouncerHiddenFraction(@FloatRange(from = 0, to = 1) float bouncerHiddenAmount) { 919 if (mBouncerHiddenFraction == bouncerHiddenAmount) { 920 return; 921 } 922 mBouncerHiddenFraction = bouncerHiddenAmount; 923 if (mState == ScrimState.DREAMING || mState == ScrimState.GLANCEABLE_HUB 924 || mState == ScrimState.GLANCEABLE_HUB_OVER_DREAM) { 925 // The dreaming and glanceable hub states requires this for the scrim calculation, so we 926 // should only trigger an update in those states. 927 applyAndDispatchState(); 928 } 929 } 930 931 /** 932 * If QS and notification scrims should not overlap, and should be clipped to each other's 933 * bounds instead. 934 */ setClipsQsScrim(boolean clipScrim)935 public void setClipsQsScrim(boolean clipScrim) { 936 if (Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) { 937 // Never clip scrims when blur is enabled, colors of UI elements are supposed to "add" 938 // up across the scrims. 939 mClipsQsScrim = false; 940 return; 941 } 942 if (clipScrim == mClipsQsScrim) { 943 return; 944 } 945 mClipsQsScrim = clipScrim; 946 for (ScrimState state : ScrimState.values()) { 947 state.setClipQsScrim(mClipsQsScrim); 948 } 949 if (mScrimBehind != null) { 950 mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim); 951 } 952 if (mState != ScrimState.UNINITIALIZED) { 953 // the clipScrimState has changed, let's reprepare ourselves 954 mState.prepare(mState); 955 applyAndDispatchState(); 956 } 957 } 958 959 @VisibleForTesting getClipQsScrim()960 public boolean getClipQsScrim() { 961 return mClipsQsScrim; 962 } 963 setOccludeAnimationPlaying(boolean occludeAnimationPlaying)964 public void setOccludeAnimationPlaying(boolean occludeAnimationPlaying) { 965 mOccludeAnimationPlaying = occludeAnimationPlaying; 966 967 for (ScrimState state : ScrimState.values()) { 968 state.setOccludeAnimationPlaying(occludeAnimationPlaying); 969 } 970 971 applyAndDispatchState(); 972 } 973 setOrAdaptCurrentAnimation(@ullable View scrim)974 private void setOrAdaptCurrentAnimation(@Nullable View scrim) { 975 if (scrim == null) { 976 return; 977 } 978 979 float alpha = getCurrentScrimAlpha(scrim); 980 boolean qsScrimPullingDown = scrim == mScrimBehind && mQsBottomVisible; 981 if (isAnimating(scrim) && !qsScrimPullingDown) { 982 // Adapt current animation. 983 ValueAnimator previousAnimator = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM); 984 float previousEndValue = (Float) scrim.getTag(TAG_END_ALPHA); 985 float previousStartValue = (Float) scrim.getTag(TAG_START_ALPHA); 986 float relativeDiff = alpha - previousEndValue; 987 float newStartValue = previousStartValue + relativeDiff; 988 scrim.setTag(TAG_START_ALPHA, newStartValue); 989 scrim.setTag(TAG_END_ALPHA, alpha); 990 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 991 } else { 992 // Set animation. 993 updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); 994 } 995 } 996 applyState()997 private void applyState() { 998 mInFrontTint = mState.getFrontTint(); 999 mBehindTint = mState.getBehindTint(); 1000 mNotificationsTint = mState.getNotifTint(); 1001 1002 mInFrontAlpha = mState.getFrontAlpha(); 1003 mBehindAlpha = mState.getBehindAlpha(); 1004 mNotificationsAlpha = mState.getNotifAlpha(); 1005 1006 assertAlphasValid(); 1007 1008 if (!mExpansionAffectsAlpha) { 1009 return; 1010 } 1011 1012 if (mState == ScrimState.UNLOCKED || mState == ScrimState.DREAMING 1013 || mState == ScrimState.GLANCEABLE_HUB_OVER_DREAM) { 1014 final boolean occluding = 1015 mOccludeAnimationPlaying || mState.mLaunchingAffordanceWithPreview; 1016 // Darken scrim as it's pulled down while unlocked. If we're unlocked but playing the 1017 // screen off/occlusion animations, ignore expansion changes while those animations 1018 // play. 1019 if (!mScreenOffAnimationController.shouldExpandNotifications() 1020 && !mAnimatingPanelExpansionOnUnlock 1021 && !occluding) { 1022 if (mTransparentScrimBackground) { 1023 mBehindAlpha = 0; 1024 mNotificationsAlpha = 0; 1025 } else if (mClipsQsScrim) { 1026 float behindFraction = getInterpolatedFraction(); 1027 behindFraction = (float) Math.pow(behindFraction, 0.8f); 1028 mBehindAlpha = 1; 1029 mNotificationsAlpha = behindFraction * getDefaultScrimAlpha(); 1030 } else { 1031 if (Flags.notificationShadeBlur()) { 1032 // TODO (b/390730594): match any spec for controlling alpha based on shade 1033 // expansion fraction. 1034 mBehindAlpha = mState.getBehindAlpha() * mPanelExpansionFraction; 1035 mBehindTint = mState.getBehindTint(); 1036 mNotificationsAlpha = mState.getNotifAlpha() * mPanelExpansionFraction; 1037 mNotificationsTint = mState.getNotifTint(); 1038 } else { 1039 mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha( 1040 mPanelExpansionFraction * getDefaultScrimAlpha()); 1041 mNotificationsAlpha = 1042 mLargeScreenShadeInterpolator.getNotificationScrimAlpha( 1043 mPanelExpansionFraction); 1044 } 1045 } 1046 mBehindTint = mState.getBehindTint(); 1047 mInFrontAlpha = 0; 1048 } 1049 1050 if ((mState == ScrimState.DREAMING || mState == ScrimState.GLANCEABLE_HUB_OVER_DREAM) 1051 && mBouncerHiddenFraction != KeyguardBouncerConstants.EXPANSION_HIDDEN) { 1052 // Bouncer is opening over dream or glanceable hub over dream. 1053 final float interpolatedFraction = 1054 BouncerPanelExpansionCalculator.aboutToShowBouncerProgress( 1055 mBouncerHiddenFraction); 1056 mBehindAlpha = MathUtils.lerp(getDefaultScrimAlpha(), mBehindAlpha, 1057 interpolatedFraction); 1058 mBehindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), 1059 mBehindTint, 1060 interpolatedFraction); 1061 } 1062 } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED 1063 || mState == ScrimState.PULSING || mState == ScrimState.GLANCEABLE_HUB) { 1064 Pair<Integer, Float> result = calculateBackStateForState(mState); 1065 int behindTint = result.first; 1066 float behindAlpha = result.second; 1067 if (mTransitionToFullShadeProgress > 0.0f) { 1068 Pair<Integer, Float> shadeResult = calculateBackStateForState( 1069 ScrimState.SHADE_LOCKED); 1070 behindAlpha = MathUtils.lerp(behindAlpha, shadeResult.second, 1071 mTransitionToFullShadeProgress); 1072 behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first, 1073 mTransitionToFullShadeProgress); 1074 } else if (mState == ScrimState.GLANCEABLE_HUB && mTransitionToFullShadeProgress == 0.0f 1075 && mBouncerHiddenFraction == KeyguardBouncerConstants.EXPANSION_HIDDEN) { 1076 // Behind scrim should not be visible when idle on the glanceable hub and neither 1077 // bouncer nor shade are showing. 1078 behindAlpha = 0f; 1079 } 1080 mInFrontAlpha = mState.getFrontAlpha(); 1081 if (mClipsQsScrim) { 1082 mNotificationsAlpha = behindAlpha; 1083 mNotificationsTint = behindTint; 1084 mBehindAlpha = 1; 1085 mBehindTint = Color.BLACK; 1086 } else { 1087 mBehindAlpha = behindAlpha; 1088 if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) { 1089 mNotificationsAlpha = MathUtils 1090 .saturate(mTransitionToLockScreenFullShadeNotificationsProgress); 1091 } else if (mState == ScrimState.SHADE_LOCKED) { 1092 // going from KEYGUARD to SHADE_LOCKED state 1093 if (Flags.notificationShadeBlur()) { 1094 mNotificationsAlpha = mState.getNotifAlpha() * getInterpolatedFraction(); 1095 } else { 1096 mNotificationsAlpha = getInterpolatedFraction(); 1097 } 1098 } else if (mState == ScrimState.GLANCEABLE_HUB 1099 && mTransitionToFullShadeProgress == 0.0f) { 1100 // Notification scrim should not be visible on the glanceable hub unless the 1101 // shade is showing or transitioning in. Otherwise the notification scrim will 1102 // be visible as the bouncer transitions in or after the notification shade 1103 // closes. 1104 mNotificationsAlpha = 0; 1105 } else { 1106 mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); 1107 } 1108 mNotificationsTint = mState.getNotifTint(); 1109 mBehindTint = behindTint; 1110 } 1111 1112 // At the end of a launch animation over the lockscreen, the state is either KEYGUARD or 1113 // SHADE_LOCKED and this code is called. We have to set the notification alpha to 0 1114 // otherwise there is a flicker to its previous value. 1115 boolean hideNotificationScrim = (mState == ScrimState.KEYGUARD 1116 && mTransitionToFullShadeProgress == 0 1117 && mQsExpansion == 0 1118 && !mClipsQsScrim); 1119 if (mKeyguardOccluded || hideNotificationScrim) { 1120 mNotificationsAlpha = 0; 1121 } 1122 } 1123 if (mState != ScrimState.UNLOCKED) { 1124 mAnimatingPanelExpansionOnUnlock = false; 1125 } 1126 1127 assertAlphasValid(); 1128 } 1129 assertAlphasValid()1130 private void assertAlphasValid() { 1131 if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { 1132 throw new IllegalStateException("Scrim opacity is NaN for state: " + mState 1133 + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " 1134 + mNotificationsAlpha); 1135 } 1136 } 1137 calculateBackStateForState(ScrimState state)1138 private Pair<Integer, Float> calculateBackStateForState(ScrimState state) { 1139 // Either darken or make the scrim transparent when pulling down the shade. 1140 float interpolatedFract = getInterpolatedFraction(); 1141 1142 float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha(); 1143 float behindAlpha; 1144 int behindTint = state.getBehindTint(); 1145 if (mDarkenWhileDragging) { 1146 behindAlpha = MathUtils.lerp(getDefaultScrimAlpha(), stateBehind, 1147 interpolatedFract); 1148 } else { 1149 behindAlpha = MathUtils.lerp(0 /* start */, stateBehind, 1150 interpolatedFract); 1151 } 1152 if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) { 1153 if (mClipsQsScrim) { 1154 behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(), 1155 state.getNotifTint(), interpolatedFract); 1156 } else { 1157 behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), 1158 state.getBehindTint(), interpolatedFract); 1159 } 1160 } 1161 if (mQsExpansion > 0) { 1162 behindAlpha = MathUtils.lerp(behindAlpha, getDefaultScrimAlpha(), mQsExpansion); 1163 float tintProgress = mQsExpansion; 1164 if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) { 1165 // this is case of - on lockscreen - going from expanded QS to bouncer. 1166 // Because mQsExpansion is already interpolated and transition between tints 1167 // is too slow, we want to speed it up and make it more aligned to bouncer 1168 // showing up progress. This issue is visible on large screens, both portrait and 1169 // split shade because then transition is between very different tints 1170 tintProgress = BouncerPanelExpansionCalculator 1171 .showBouncerProgress(mPanelExpansionFraction); 1172 } 1173 int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint() 1174 : ScrimState.SHADE_LOCKED.getBehindTint(); 1175 behindTint = ColorUtils.blendARGB(behindTint, stateTint, tintProgress); 1176 } 1177 1178 // If the keyguard is going away, we should not be opaque. 1179 if (mKeyguardStateController.isKeyguardGoingAway()) { 1180 behindAlpha = 0f; 1181 } 1182 1183 return new Pair<>(behindTint, behindAlpha); 1184 } 1185 1186 applyAndDispatchState()1187 private void applyAndDispatchState() { 1188 applyState(); 1189 if (mUpdatePending) { 1190 return; 1191 } 1192 setOrAdaptCurrentAnimation(mScrimBehind); 1193 setOrAdaptCurrentAnimation(mNotificationsScrim); 1194 setOrAdaptCurrentAnimation(mScrimInFront); 1195 dispatchBackScrimState(mScrimBehind.getViewAlpha()); 1196 } 1197 1198 /** 1199 * Sets the front scrim opacity in AOD so it's not as bright. 1200 * <p> 1201 * Displays usually don't support multiple dimming settings when in low power mode. 1202 * The workaround is to modify the front scrim opacity when in AOD, so it's not as 1203 * bright when you're at the movies or lying down on bed. 1204 * <p> 1205 * This value will be lost during transitions and only updated again after the the 1206 * device is dozing when the light sensor is on. 1207 */ setAodFrontScrimAlpha(float alpha)1208 public void setAodFrontScrimAlpha(float alpha) { 1209 if (mInFrontAlpha != alpha && shouldUpdateFrontScrimAlpha()) { 1210 mInFrontAlpha = alpha; 1211 updateScrims(); 1212 } 1213 1214 mState.AOD.setAodFrontScrimAlpha(alpha); 1215 mState.PULSING.setAodFrontScrimAlpha(alpha); 1216 } 1217 shouldUpdateFrontScrimAlpha()1218 private boolean shouldUpdateFrontScrimAlpha() { 1219 if (mState == ScrimState.AOD 1220 && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) { 1221 return true; 1222 } 1223 1224 return mState == ScrimState.PULSING; 1225 } 1226 1227 /** 1228 * If the lock screen sensor is active. 1229 */ setWakeLockScreenSensorActive(boolean active)1230 public void setWakeLockScreenSensorActive(boolean active) { 1231 for (ScrimState state : ScrimState.values()) { 1232 state.setWakeLockScreenSensorActive(active); 1233 } 1234 1235 if (mState == ScrimState.PULSING) { 1236 float newBehindAlpha = mState.getBehindAlpha(); 1237 if (mBehindAlpha != newBehindAlpha) { 1238 mBehindAlpha = newBehindAlpha; 1239 if (isNaN(mBehindAlpha)) { 1240 throw new IllegalStateException("Scrim opacity is NaN for state: " + mState 1241 + ", back: " + mBehindAlpha); 1242 } 1243 updateScrims(); 1244 } 1245 } 1246 } 1247 scheduleUpdate()1248 protected void scheduleUpdate() { 1249 if (mUpdatePending || mScrimBehind == null) return; 1250 1251 // Make sure that a frame gets scheduled. 1252 mScrimBehind.invalidate(); 1253 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 1254 mUpdatePending = true; 1255 } 1256 updateScrims()1257 protected void updateScrims() { 1258 // Make sure we have the right gradients and their opacities will satisfy GAR. 1259 if (mNeedsDrawableColorUpdate) { 1260 mNeedsDrawableColorUpdate = false; 1261 // Only animate scrim color if the scrim view is actually visible 1262 boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen; 1263 boolean animateBehindScrim = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen; 1264 boolean animateScrimNotifications = mNotificationsScrim.getViewAlpha() != 0 1265 && !mBlankScreen; 1266 1267 mScrimInFront.setColors(mColors, animateScrimInFront); 1268 mScrimBehind.setColors(mColors, animateBehindScrim); 1269 mNotificationsScrim.setColors(mColors, animateScrimNotifications); 1270 1271 dispatchBackScrimState(mScrimBehind.getViewAlpha()); 1272 } 1273 if (Flags.bouncerUiRevamp()) { 1274 // Blur the notification scrim as needed. The blur is needed only when we show the 1275 // expanded shade behind the bouncer. Without it, the notification scrim outline is 1276 // visible behind the bouncer. 1277 mNotificationsScrim.setBlurRadius(mState.getNotifBlurRadius()); 1278 } 1279 1280 // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim. 1281 boolean hideFlagShowWhenLockedActivities = 1282 (mState == ScrimState.PULSING || mState == ScrimState.AOD) 1283 && mKeyguardOccluded; 1284 if (hideFlagShowWhenLockedActivities) { 1285 mBehindAlpha = 1; 1286 } 1287 // Prevent notification scrim flicker when transitioning away from keyguard. 1288 if (mKeyguardStateController.isKeyguardGoingAway()) { 1289 mNotificationsAlpha = 0; 1290 } 1291 1292 // Prevent flickering for activities above keyguard and quick settings in keyguard. 1293 if (mKeyguardOccluded 1294 && (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED)) { 1295 mBehindAlpha = 0; 1296 mNotificationsAlpha = 0; 1297 } 1298 1299 setScrimAlpha(mScrimInFront, mInFrontAlpha); 1300 setScrimAlpha(mScrimBehind, mBehindAlpha); 1301 setScrimAlpha(mNotificationsScrim, mNotificationsAlpha); 1302 1303 // The animation could have all already finished, let's call onFinished just in case 1304 onFinished(mState); 1305 dispatchScrimsVisible(); 1306 } 1307 dispatchBackScrimState(float alpha)1308 private void dispatchBackScrimState(float alpha) { 1309 // When clipping QS, the notification scrim is the one that feels behind. 1310 // mScrimBehind will be drawing black and its opacity will always be 1. 1311 if (mClipsQsScrim && mQsBottomVisible) { 1312 alpha = mNotificationsAlpha; 1313 } 1314 mScrimStateListener.accept(mState, alpha, mColors); 1315 } 1316 dispatchScrimsVisible()1317 private void dispatchScrimsVisible() { 1318 final ScrimView backScrim = mClipsQsScrim ? mNotificationsScrim : mScrimBehind; 1319 final int currentScrimVisibility; 1320 if (mScrimInFront.getViewAlpha() == 1 || backScrim.getViewAlpha() == 1) { 1321 currentScrimVisibility = OPAQUE; 1322 } else if (mScrimInFront.getViewAlpha() == 0 && backScrim.getViewAlpha() == 0) { 1323 currentScrimVisibility = TRANSPARENT; 1324 } else { 1325 currentScrimVisibility = SEMI_TRANSPARENT; 1326 } 1327 1328 if (mScrimsVisibility != currentScrimVisibility) { 1329 mScrimsVisibility = currentScrimVisibility; 1330 mScrimVisibleListener.accept(currentScrimVisibility); 1331 } 1332 } 1333 getInterpolatedFraction()1334 private float getInterpolatedFraction() { 1335 if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) { 1336 return BouncerPanelExpansionCalculator 1337 .aboutToShowBouncerProgress(mPanelExpansionFraction); 1338 } 1339 return ShadeInterpolation.getNotificationScrimAlpha(mPanelExpansionFraction); 1340 } 1341 setScrimAlpha(ScrimView scrim, float alpha)1342 private void setScrimAlpha(ScrimView scrim, float alpha) { 1343 if (alpha == 0f) { 1344 scrim.setClickable(false); 1345 } else { 1346 // Eat touch events (unless dozing). 1347 scrim.setClickable(mState != ScrimState.AOD); 1348 } 1349 updateScrim(scrim, alpha); 1350 } 1351 getScrimName(ScrimView scrim)1352 private String getScrimName(ScrimView scrim) { 1353 if (scrim == mScrimInFront) { 1354 return "front_scrim"; 1355 } else if (scrim == mScrimBehind) { 1356 return "behind_scrim"; 1357 } else if (scrim == mNotificationsScrim) { 1358 return "notifications_scrim"; 1359 } 1360 return "unknown_scrim"; 1361 } 1362 updateScrimColor(View scrim, float alpha, int tint)1363 private void updateScrimColor(View scrim, float alpha, int tint) { 1364 alpha = Math.max(0, Math.min(1.0f, alpha)); 1365 if (scrim instanceof ScrimView) { 1366 ScrimView scrimView = (ScrimView) scrim; 1367 if (DEBUG_MODE) { 1368 tint = getDebugScrimTint(scrimView); 1369 } 1370 1371 TrackTracer.instantForGroup("scrim", getScrimName(scrimView) + "_alpha", 1372 (int) (alpha * 255)); 1373 TrackTracer.instantForGroup("scrim", getScrimName(scrimView) + "_tint", 1374 Color.alpha(tint)); 1375 scrimView.setTint(tint); 1376 if (!mIsBouncerToGoneTransitionRunning) { 1377 scrimView.setViewAlpha(alpha); 1378 } 1379 } else { 1380 scrim.setAlpha(alpha); 1381 } 1382 dispatchScrimsVisible(); 1383 } 1384 getDebugScrimTint(ScrimView scrim)1385 private int getDebugScrimTint(ScrimView scrim) { 1386 if (scrim == mScrimBehind) return DEBUG_BEHIND_TINT; 1387 if (scrim == mScrimInFront) return DEBUG_FRONT_TINT; 1388 if (scrim == mNotificationsScrim) return DEBUG_NOTIFICATIONS_TINT; 1389 throw new RuntimeException("scrim can't be matched with known scrims"); 1390 } 1391 startScrimAnimation(final View scrim, float current)1392 private void startScrimAnimation(final View scrim, float current) { 1393 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 1394 if (mAnimatorListener != null) { 1395 anim.addListener(mAnimatorListener); 1396 } 1397 final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() : 1398 Color.TRANSPARENT; 1399 anim.addUpdateListener(animation -> { 1400 final float startAlpha = (Float) scrim.getTag(TAG_START_ALPHA); 1401 final float animAmount = (float) animation.getAnimatedValue(); 1402 final int finalScrimTint = getCurrentScrimTint(scrim); 1403 final float finalScrimAlpha = getCurrentScrimAlpha(scrim); 1404 float alpha = MathUtils.lerp(startAlpha, finalScrimAlpha, animAmount); 1405 alpha = MathUtils.constrain(alpha, 0f, 1f); 1406 int tint = ColorUtils.blendARGB(initialScrimTint, finalScrimTint, animAmount); 1407 updateScrimColor(scrim, alpha, tint); 1408 dispatchScrimsVisible(); 1409 }); 1410 anim.setInterpolator(mInterpolator); 1411 anim.setStartDelay(mAnimationDelay); 1412 anim.setDuration(mAnimationDuration); 1413 anim.addListener(new AnimatorListenerAdapter() { 1414 private final ScrimState mLastState = mState; 1415 private final Callback mLastCallback = mCallback; 1416 1417 @Override 1418 public void onAnimationEnd(Animator animation) { 1419 scrim.setTag(TAG_KEY_ANIM, null); 1420 onFinished(mLastCallback, mLastState); 1421 1422 dispatchScrimsVisible(); 1423 } 1424 }); 1425 1426 // Cache alpha values because we might want to update this animator in the future if 1427 // the user expands the panel while the animation is still running. 1428 scrim.setTag(TAG_START_ALPHA, current); 1429 scrim.setTag(TAG_END_ALPHA, getCurrentScrimAlpha(scrim)); 1430 1431 scrim.setTag(TAG_KEY_ANIM, anim); 1432 anim.start(); 1433 } 1434 getCurrentScrimAlpha(View scrim)1435 private float getCurrentScrimAlpha(View scrim) { 1436 if (scrim == mScrimInFront) { 1437 return mInFrontAlpha; 1438 } else if (scrim == mScrimBehind) { 1439 return mBehindAlpha; 1440 } else if (scrim == mNotificationsScrim) { 1441 return mNotificationsAlpha; 1442 } else { 1443 throw new IllegalArgumentException("Unknown scrim view"); 1444 } 1445 } 1446 getCurrentScrimTint(View scrim)1447 private int getCurrentScrimTint(View scrim) { 1448 if (scrim == mScrimInFront) { 1449 return mInFrontTint; 1450 } else if (scrim == mScrimBehind) { 1451 return mBehindTint; 1452 } else if (scrim == mNotificationsScrim) { 1453 return mNotificationsTint; 1454 } else { 1455 throw new IllegalArgumentException("Unknown scrim view"); 1456 } 1457 } 1458 1459 @Override onPreDraw()1460 public boolean onPreDraw() { 1461 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 1462 mUpdatePending = false; 1463 if (mCallback != null) { 1464 mCallback.onStart(); 1465 } 1466 updateScrims(); 1467 return true; 1468 } 1469 1470 /** 1471 * @param state that finished 1472 */ onFinished(ScrimState state)1473 private void onFinished(ScrimState state) { 1474 onFinished(mCallback, state); 1475 } 1476 onFinished(Callback callback, ScrimState state)1477 private void onFinished(Callback callback, ScrimState state) { 1478 if (mPendingFrameCallback != null) { 1479 // No animations can finish while we're waiting on the blanking to finish 1480 return; 1481 1482 } 1483 if (isAnimating(mScrimBehind) 1484 || isAnimating(mNotificationsScrim) 1485 || isAnimating(mScrimInFront)) { 1486 if (callback != null && callback != mCallback) { 1487 // Since we only notify the callback that we're finished once everything has 1488 // finished, we need to make sure that any changing callbacks are also invoked 1489 callback.onFinished(); 1490 } 1491 return; 1492 } 1493 if (mWakeLockHeld) { 1494 mWakeLock.release(TAG); 1495 mWakeLockHeld = false; 1496 } 1497 1498 if (callback != null) { 1499 callback.onFinished(); 1500 1501 if (callback == mCallback) { 1502 mCallback = null; 1503 } 1504 } 1505 1506 // When unlocking with fingerprint, we'll fade the scrims from black to transparent. 1507 // At the end of the animation we need to remove the tint. 1508 if (state == ScrimState.UNLOCKED) { 1509 mInFrontTint = Color.TRANSPARENT; 1510 mBehindTint = mState.getBehindTint(); 1511 mNotificationsTint = mState.getNotifTint(); 1512 updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint); 1513 updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint); 1514 updateScrimColor(mNotificationsScrim, mNotificationsAlpha, mNotificationsTint); 1515 } 1516 } 1517 isAnimating(@ullable View scrim)1518 private boolean isAnimating(@Nullable View scrim) { 1519 return scrim != null && scrim.getTag(TAG_KEY_ANIM) != null; 1520 } 1521 1522 @VisibleForTesting setAnimatorListener(Animator.AnimatorListener animatorListener)1523 void setAnimatorListener(Animator.AnimatorListener animatorListener) { 1524 mAnimatorListener = animatorListener; 1525 } 1526 updateScrim(ScrimView scrim, float alpha)1527 private void updateScrim(ScrimView scrim, float alpha) { 1528 final float currentAlpha = scrim.getViewAlpha(); 1529 1530 ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM); 1531 if (previousAnimator != null) { 1532 // Previous animators should always be cancelled. Not doing so would cause 1533 // overlap, especially on states that don't animate, leading to flickering, 1534 // and in the worst case, an internal state that doesn't represent what 1535 // transitionTo requested. 1536 cancelAnimator(previousAnimator); 1537 } 1538 1539 if (mPendingFrameCallback != null) { 1540 // Display is off and we're waiting. 1541 return; 1542 } else if (mBlankScreen) { 1543 // Need to blank the display before continuing. 1544 blankDisplay(); 1545 return; 1546 } else if (!mScreenBlankingCallbackCalled) { 1547 // Not blanking the screen. Letting the callback know that we're ready 1548 // to replace what was on the screen before. 1549 if (mCallback != null) { 1550 mCallback.onDisplayBlanked(); 1551 mScreenBlankingCallbackCalled = true; 1552 } 1553 } 1554 1555 if (scrim == mScrimBehind) { 1556 dispatchBackScrimState(alpha); 1557 } 1558 1559 final boolean wantsAlphaUpdate = alpha != currentAlpha; 1560 final boolean wantsTintUpdate = scrim.getTint() != getCurrentScrimTint(scrim); 1561 1562 if (wantsAlphaUpdate || wantsTintUpdate) { 1563 if (mAnimateChange) { 1564 startScrimAnimation(scrim, currentAlpha); 1565 } else { 1566 // update the alpha directly 1567 updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); 1568 } 1569 } 1570 } 1571 cancelAnimator(ValueAnimator previousAnimator)1572 private void cancelAnimator(ValueAnimator previousAnimator) { 1573 if (previousAnimator != null) { 1574 previousAnimator.cancel(); 1575 } 1576 } 1577 blankDisplay()1578 private void blankDisplay() { 1579 updateScrimColor(mScrimInFront, 1, Color.BLACK); 1580 1581 // Notify callback that the screen is completely black and we're 1582 // ready to change the display power mode 1583 mPendingFrameCallback = () -> { 1584 if (mCallback != null) { 1585 mCallback.onDisplayBlanked(); 1586 mScreenBlankingCallbackCalled = true; 1587 } 1588 1589 mBlankingTransitionRunnable = () -> { 1590 mBlankingTransitionRunnable = null; 1591 mPendingFrameCallback = null; 1592 mBlankScreen = false; 1593 // Try again. 1594 updateScrims(); 1595 }; 1596 1597 // Setting power states can happen after we push out the frame. Make sure we 1598 // stay fully opaque until the power state request reaches the lower levels. 1599 final int delay = mScreenOn ? 32 : 500; 1600 if (DEBUG) { 1601 Log.d(TAG, "Fading out scrims with delay: " + delay); 1602 } 1603 mHandler.postDelayed(mBlankingTransitionRunnable, delay); 1604 }; 1605 doOnTheNextFrame(mPendingFrameCallback); 1606 } 1607 1608 /** 1609 * Executes a callback after the frame has hit the display. 1610 * 1611 * @param callback What to run. 1612 */ 1613 @VisibleForTesting doOnTheNextFrame(Runnable callback)1614 protected void doOnTheNextFrame(Runnable callback) { 1615 // Just calling View#postOnAnimation isn't enough because the frame might not have reached 1616 // the display yet. A timeout is the safest solution. 1617 mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */); 1618 } 1619 updateThemeColors()1620 private void updateThemeColors() { 1621 if (mScrimBehind == null) return; 1622 int background = mContext.getColor( 1623 com.android.internal.R.color.materialColorSurfaceDim); 1624 int accent = mContext.getColor( 1625 com.android.internal.R.color.materialColorPrimary); 1626 mColors.setMainColor(background); 1627 mColors.setSecondaryColor(accent); 1628 final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background); 1629 mColors.setSupportsDarkText(isBackgroundLight); 1630 1631 int surface = mContext.getColor( 1632 com.android.internal.R.color.materialColorSurface); 1633 for (ScrimState state : ScrimState.values()) { 1634 state.setSurfaceColor(surface); 1635 } 1636 1637 mNeedsDrawableColorUpdate = true; 1638 } 1639 onThemeChanged()1640 private void onThemeChanged() { 1641 updateThemeColors(); 1642 scheduleUpdate(); 1643 } 1644 1645 @Override dump(PrintWriter pw, String[] args)1646 public void dump(PrintWriter pw, String[] args) { 1647 pw.println(" ScrimController: "); 1648 pw.print(" state: "); 1649 pw.println(mState); 1650 pw.println(" mClipQsScrim = " + mState.mClipQsScrim); 1651 1652 pw.print(" frontScrim:"); 1653 pw.print(" viewAlpha="); 1654 pw.print(mScrimInFront.getViewAlpha()); 1655 pw.print(" alpha="); 1656 pw.print(mInFrontAlpha); 1657 pw.print(" tint=0x"); 1658 pw.println(Integer.toHexString(mScrimInFront.getTint())); 1659 1660 pw.print(" behindScrim:"); 1661 pw.print(" viewAlpha="); 1662 pw.print(mScrimBehind.getViewAlpha()); 1663 pw.print(" alpha="); 1664 pw.print(mBehindAlpha); 1665 pw.print(" tint=0x"); 1666 pw.println(Integer.toHexString(mScrimBehind.getTint())); 1667 1668 pw.print(" notificationsScrim:"); 1669 pw.print(" viewAlpha="); 1670 pw.print(mNotificationsScrim.getViewAlpha()); 1671 pw.print(" alpha="); 1672 pw.print(mNotificationsAlpha); 1673 pw.print(" tint=0x"); 1674 pw.println(Integer.toHexString(mNotificationsScrim.getTint())); 1675 pw.print(" expansionProgress="); 1676 pw.println(mTransitionToLockScreenFullShadeNotificationsProgress); 1677 1678 pw.print(" mDefaultScrimAlpha="); 1679 pw.println(getDefaultScrimAlpha()); 1680 pw.print(" mPanelExpansionFraction="); 1681 pw.println(mPanelExpansionFraction); 1682 pw.print(" mExpansionAffectsAlpha="); 1683 pw.println(mExpansionAffectsAlpha); 1684 } 1685 1686 /** 1687 * Interrupts blanking transitions once the display notifies that it's already on. 1688 */ onScreenTurnedOn()1689 public void onScreenTurnedOn() { 1690 mScreenOn = true; 1691 if (mHandler.hasCallbacks(mBlankingTransitionRunnable)) { 1692 if (DEBUG) { 1693 Log.d(TAG, "Shorter blanking because screen turned on. All good."); 1694 } 1695 mHandler.removeCallbacks(mBlankingTransitionRunnable); 1696 mBlankingTransitionRunnable.run(); 1697 } 1698 } 1699 onScreenTurnedOff()1700 public void onScreenTurnedOff() { 1701 mScreenOn = false; 1702 } 1703 isScreenOn()1704 public boolean isScreenOn() { 1705 return mScreenOn; 1706 } 1707 setExpansionAffectsAlpha(boolean expansionAffectsAlpha)1708 public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) { 1709 mExpansionAffectsAlpha = expansionAffectsAlpha; 1710 } 1711 setKeyguardOccluded(boolean keyguardOccluded)1712 public void setKeyguardOccluded(boolean keyguardOccluded) { 1713 if (mKeyguardOccluded == keyguardOccluded) { 1714 return; 1715 } 1716 mKeyguardOccluded = keyguardOccluded; 1717 updateScrims(); 1718 } 1719 setKeyguardFadingAway(boolean fadingAway, long duration)1720 private void setKeyguardFadingAway(boolean fadingAway, long duration) { 1721 for (ScrimState state : ScrimState.values()) { 1722 state.setKeyguardFadingAway(fadingAway, duration); 1723 } 1724 } 1725 setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview)1726 public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) { 1727 for (ScrimState state : ScrimState.values()) { 1728 state.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); 1729 } 1730 } 1731 1732 public interface Callback { onStart()1733 default void onStart() { 1734 } 1735 onDisplayBlanked()1736 default void onDisplayBlanked() { 1737 } 1738 onFinished()1739 default void onFinished() { 1740 } 1741 onCancelled()1742 default void onCancelled() { 1743 } 1744 } 1745 1746 /** 1747 * Simple keyguard callback that updates scrims when keyguard visibility changes. 1748 */ 1749 private class KeyguardVisibilityCallback extends KeyguardUpdateMonitorCallback { 1750 1751 @Override onKeyguardVisibilityChanged(boolean visible)1752 public void onKeyguardVisibilityChanged(boolean visible) { 1753 mNeedsDrawableColorUpdate = true; 1754 scheduleUpdate(); 1755 } 1756 } 1757 } 1758