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