1 /* 2 * Copyright (C) 2022 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.quickstep; 18 19 import static android.view.RemoteAnimationTarget.MODE_CLOSING; 20 import static android.view.RemoteAnimationTarget.MODE_OPENING; 21 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 22 23 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; 24 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL; 25 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS; 26 import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION; 27 28 import android.animation.Animator; 29 import android.animation.AnimatorListenerAdapter; 30 import android.animation.AnimatorSet; 31 import android.animation.ValueAnimator; 32 import android.content.ComponentCallbacks; 33 import android.content.res.Configuration; 34 import android.graphics.Matrix; 35 import android.graphics.PointF; 36 import android.graphics.Rect; 37 import android.graphics.RectF; 38 import android.os.Handler; 39 import android.os.RemoteException; 40 import android.util.Log; 41 import android.util.Pair; 42 import android.view.Choreographer; 43 import android.view.IRemoteAnimationFinishedCallback; 44 import android.view.IRemoteAnimationRunner; 45 import android.view.RemoteAnimationTarget; 46 import android.view.SurfaceControl; 47 import android.view.View; 48 import android.view.animation.DecelerateInterpolator; 49 import android.view.animation.Interpolator; 50 import android.window.BackEvent; 51 import android.window.BackMotionEvent; 52 import android.window.BackProgressAnimator; 53 import android.window.IOnBackInvokedCallback; 54 55 import com.android.app.animation.Interpolators; 56 import com.android.internal.policy.SystemBarUtils; 57 import com.android.internal.view.AppearanceRegion; 58 import com.android.launcher3.AbstractFloatingView; 59 import com.android.launcher3.BubbleTextView; 60 import com.android.launcher3.QuickstepTransitionManager; 61 import com.android.launcher3.R; 62 import com.android.launcher3.Utilities; 63 import com.android.launcher3.taskbar.LauncherTaskbarUIController; 64 import com.android.launcher3.uioverrides.QuickstepLauncher; 65 import com.android.launcher3.widget.LauncherAppWidgetHostView; 66 import com.android.quickstep.util.RectFSpringAnim; 67 import com.android.systemui.shared.system.QuickStepContract; 68 69 import java.lang.ref.WeakReference; 70 71 /** 72 * Controls the animation of swiping back and returning to launcher. 73 * 74 * This is a two part animation. The first part is an animation that tracks gesture location to 75 * scale and move the leaving app window. Once the gesture is committed, the second part takes over 76 * the app window and plays the rest of app close transitions in one go. 77 * 78 * This animation is used only for apps that enable back dispatching via 79 * {@link android.window.OnBackInvokedDispatcher}. The controller registers 80 * an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back 81 * navigation to launcher starts. 82 * 83 * Apps using the legacy back dispatching will keep triggering the WALLPAPER_OPEN remote 84 * transition registered in {@link QuickstepTransitionManager}. 85 * 86 */ 87 public class LauncherBackAnimationController { 88 private static final int SCRIM_FADE_DURATION = 233; 89 private static final float MIN_WINDOW_SCALE = 0.85f; 90 private static final float MAX_SCRIM_ALPHA_DARK = 0.8f; 91 private static final float MAX_SCRIM_ALPHA_LIGHT = 0.2f; 92 93 private final QuickstepTransitionManager mQuickstepTransitionManager; 94 private final Matrix mTransformMatrix = new Matrix(); 95 /** The window position at the beginning of the back animation. */ 96 private final Rect mStartRect = new Rect(); 97 /** The current window position. */ 98 private final RectF mCurrentRect = new RectF(); 99 private final QuickstepLauncher mLauncher; 100 private final int mWindowScaleMarginX; 101 private float mWindowScaleEndCornerRadius; 102 private float mWindowScaleStartCornerRadius; 103 private int mStatusBarHeight; 104 private final Interpolator mProgressInterpolator = Interpolators.BACK_GESTURE; 105 private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator(); 106 private final PointF mInitialTouchPos = new PointF(); 107 108 private RemoteAnimationTarget mBackTarget; 109 private RemoteAnimationTarget mLauncherTarget; 110 private View mLauncherTargetView; 111 private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); 112 private boolean mSpringAnimationInProgress = false; 113 private boolean mAnimatorSetInProgress = false; 114 private float mBackProgress = 0; 115 private boolean mBackInProgress = false; 116 private OnBackInvokedCallbackStub mBackCallback; 117 private IRemoteAnimationFinishedCallback mAnimationFinishedCallback; 118 private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); 119 private SurfaceControl mScrimLayer; 120 private ValueAnimator mScrimAlphaAnimator; 121 private float mScrimAlpha; 122 private boolean mOverridingStatusBarFlags; 123 124 private final ComponentCallbacks mComponentCallbacks = new ComponentCallbacks() { 125 @Override 126 public void onConfigurationChanged(Configuration newConfig) { 127 loadResources(); 128 } 129 130 @Override 131 public void onLowMemory() {} 132 }; 133 LauncherBackAnimationController( QuickstepLauncher launcher, QuickstepTransitionManager quickstepTransitionManager)134 public LauncherBackAnimationController( 135 QuickstepLauncher launcher, 136 QuickstepTransitionManager quickstepTransitionManager) { 137 mLauncher = launcher; 138 mQuickstepTransitionManager = quickstepTransitionManager; 139 loadResources(); 140 mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize( 141 R.dimen.swipe_back_window_scale_x_margin); 142 } 143 144 /** 145 * Registers {@link IOnBackInvokedCallback} to receive back dispatches from shell. 146 * @param handler Handler to the thread to run the animations on. 147 */ registerBackCallbacks(Handler handler)148 public void registerBackCallbacks(Handler handler) { 149 mBackCallback = new OnBackInvokedCallbackStub(handler, mProgressAnimator, 150 mProgressInterpolator, this); 151 SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback, 152 new RemoteAnimationRunnerStub(this)); 153 } 154 155 private static class OnBackInvokedCallbackStub extends IOnBackInvokedCallback.Stub { 156 private Handler mHandler; 157 private BackProgressAnimator mProgressAnimator; 158 private final Interpolator mProgressInterpolator; 159 // LauncherBackAnimationController has strong reference to Launcher activity, the binder 160 // callback should not hold strong reference to it to avoid memory leak. 161 private WeakReference<LauncherBackAnimationController> mControllerRef; 162 OnBackInvokedCallbackStub( Handler handler, BackProgressAnimator progressAnimator, Interpolator progressInterpolator, LauncherBackAnimationController controller)163 private OnBackInvokedCallbackStub( 164 Handler handler, 165 BackProgressAnimator progressAnimator, 166 Interpolator progressInterpolator, 167 LauncherBackAnimationController controller) { 168 mHandler = handler; 169 mProgressAnimator = progressAnimator; 170 mProgressInterpolator = progressInterpolator; 171 mControllerRef = new WeakReference<>(controller); 172 } 173 174 @Override onBackCancelled()175 public void onBackCancelled() { 176 mHandler.post(() -> { 177 LauncherBackAnimationController controller = mControllerRef.get(); 178 if (controller != null) { 179 mProgressAnimator.onBackCancelled(controller::onCancelFinished); 180 } 181 }); 182 } 183 184 @Override onBackInvoked()185 public void onBackInvoked() { 186 mHandler.post(() -> { 187 LauncherBackAnimationController controller = mControllerRef.get(); 188 if (controller != null) { 189 controller.startTransition(); 190 } 191 mProgressAnimator.reset(); 192 }); 193 } 194 195 @Override onBackProgressed(BackMotionEvent backMotionEvent)196 public void onBackProgressed(BackMotionEvent backMotionEvent) { 197 mHandler.post(() -> { 198 LauncherBackAnimationController controller = mControllerRef.get(); 199 if (controller == null 200 || controller.mLauncher == null 201 || !controller.mLauncher.isStarted()) { 202 // Skip animating back progress if Launcher isn't visible yet. 203 return; 204 } 205 mProgressAnimator.onBackProgressed(backMotionEvent); 206 }); 207 } 208 209 @Override onBackStarted(BackMotionEvent backEvent)210 public void onBackStarted(BackMotionEvent backEvent) { 211 mHandler.post(() -> { 212 LauncherBackAnimationController controller = mControllerRef.get(); 213 if (controller != null) { 214 controller.startBack(backEvent); 215 mProgressAnimator.onBackStarted(backEvent, event -> { 216 float backProgress = event.getProgress(); 217 controller.mBackProgress = 218 mProgressInterpolator.getInterpolation(backProgress); 219 controller.updateBackProgress(controller.mBackProgress, event); 220 }); 221 } 222 }); 223 } 224 225 @Override setTriggerBack(boolean triggerBack)226 public void setTriggerBack(boolean triggerBack) { 227 // TODO(b/261654570): track touch from the Launcher process. 228 } 229 } 230 231 private static class RemoteAnimationRunnerStub extends IRemoteAnimationRunner.Stub { 232 233 // LauncherBackAnimationController has strong reference to Launcher activity, the binder 234 // callback should not hold strong reference to it to avoid memory leak. 235 private WeakReference<LauncherBackAnimationController> mControllerRef; 236 RemoteAnimationRunnerStub(LauncherBackAnimationController controller)237 private RemoteAnimationRunnerStub(LauncherBackAnimationController controller) { 238 mControllerRef = new WeakReference<>(controller); 239 } 240 241 @Override onAnimationStart(int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback)242 public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, 243 RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, 244 IRemoteAnimationFinishedCallback finishedCallback) { 245 LauncherBackAnimationController controller = mControllerRef.get(); 246 if (controller == null) { 247 return; 248 } 249 for (final RemoteAnimationTarget target : apps) { 250 if (MODE_CLOSING == target.mode) { 251 controller.mBackTarget = target; 252 } 253 if (MODE_OPENING == target.mode) { 254 controller.mLauncherTarget = target; 255 } 256 } 257 controller.mAnimationFinishedCallback = finishedCallback; 258 } 259 260 @Override onAnimationCancelled()261 public void onAnimationCancelled() {} 262 } 263 onCancelFinished()264 private void onCancelFinished() { 265 customizeStatusBarAppearance(false); 266 finishAnimation(); 267 } 268 269 /** Unregisters the back to launcher callback in shell. */ unregisterBackCallbacks()270 public void unregisterBackCallbacks() { 271 if (mBackCallback != null) { 272 SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback(mBackCallback); 273 } 274 mProgressAnimator.reset(); 275 mBackCallback = null; 276 } 277 startBack(BackMotionEvent backEvent)278 private void startBack(BackMotionEvent backEvent) { 279 // in case we're still animating an onBackCancelled event, let's remove the finish- 280 // callback from the progress animator to prevent calling finishAnimation() before 281 // restarting a new animation 282 // Side note: startBack is never called during the post-commit phase if the back gesture 283 // was committed (not cancelled). BackAnimationController prevents that. Therefore we 284 // don't have to handle that case. 285 mProgressAnimator.removeOnBackCancelledFinishCallback(); 286 287 mBackInProgress = true; 288 RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget(); 289 290 if (appTarget == null || appTarget.leash == null || !appTarget.leash.isValid()) { 291 return; 292 } 293 294 mTransaction 295 .show(appTarget.leash) 296 .setAnimationTransaction(); 297 mBackTarget = appTarget; 298 mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); 299 300 mStartRect.set(appTarget.windowConfiguration.getMaxBounds()); 301 302 // inset bottom in case of pinned taskbar being present 303 mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom); 304 305 mLauncherTargetView = mQuickstepTransitionManager.findLauncherView( 306 new RemoteAnimationTarget[]{ mBackTarget }); 307 setLauncherTargetViewVisible(false); 308 mCurrentRect.set(mStartRect); 309 if (mScrimLayer == null) { 310 addScrimLayer(); 311 } 312 applyTransaction(); 313 } 314 setLauncherTargetViewVisible(boolean isVisible)315 private void setLauncherTargetViewVisible(boolean isVisible) { 316 if (mLauncherTargetView instanceof BubbleTextView) { 317 ((BubbleTextView) mLauncherTargetView).setIconVisible(isVisible); 318 } else if (mLauncherTargetView instanceof LauncherAppWidgetHostView) { 319 mLauncherTargetView.setAlpha(isVisible ? 1f : 0f); 320 } 321 } 322 addScrimLayer()323 void addScrimLayer() { 324 SurfaceControl parent = mLauncherTarget != null ? mLauncherTarget.leash : null; 325 if (parent == null || !parent.isValid()) { 326 // Parent surface is not ready at the moment. Retry later. 327 return; 328 } 329 boolean isDarkTheme = Utilities.isDarkTheme(mLauncher); 330 mScrimLayer = new SurfaceControl.Builder() 331 .setName("Back to launcher background scrim") 332 .setCallsite("LauncherBackAnimationController") 333 .setColorLayer() 334 .setParent(parent) 335 .setOpaque(false) 336 .setHidden(false) 337 .build(); 338 final float[] colorComponents = new float[] { 0f, 0f, 0f }; 339 mScrimAlpha = (isDarkTheme) 340 ? MAX_SCRIM_ALPHA_DARK : MAX_SCRIM_ALPHA_LIGHT; 341 mTransaction 342 .setColor(mScrimLayer, colorComponents) 343 .setAlpha(mScrimLayer, mScrimAlpha) 344 .show(mScrimLayer); 345 } 346 removeScrimLayer()347 void removeScrimLayer() { 348 if (mScrimLayer == null) { 349 return; 350 } 351 if (mScrimLayer.isValid()) { 352 mTransaction.remove(mScrimLayer); 353 applyTransaction(); 354 } 355 mScrimLayer = null; 356 } 357 updateBackProgress(float progress, BackEvent event)358 private void updateBackProgress(float progress, BackEvent event) { 359 if (!mBackInProgress || mBackTarget == null) { 360 return; 361 } 362 if (mScrimLayer == null) { 363 // Scrim hasn't been attached yet. Let's attach it. 364 addScrimLayer(); 365 } 366 float screenWidth = mStartRect.width(); 367 float screenHeight = mStartRect.height(); 368 float width = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth; 369 float height = screenHeight / screenWidth * width; 370 371 // Base the window movement in the Y axis on the touch movement in the Y axis. 372 float rawYDelta = event.getTouchY() - mInitialTouchPos.y; 373 float yDirection = rawYDelta < 0 ? -1 : 1; 374 // limit yDelta interpretation to 1/2 of screen height in either direction 375 float deltaYRatio = Math.min(screenHeight / 2f, Math.abs(rawYDelta)) / (screenHeight / 2f); 376 float interpolatedYRatio = mVerticalMoveInterpolator.getInterpolation(deltaYRatio); 377 // limit y-shift so surface never passes 8dp screen margin 378 float deltaY = yDirection * interpolatedYRatio * Math.max(0f, (screenHeight - height) 379 / 2f - mWindowScaleMarginX); 380 // Move the window along the Y axis. 381 float top = (screenHeight - height) * 0.5f + deltaY; 382 // Move the window along the X axis. 383 float left = event.getSwipeEdge() == BackEvent.EDGE_RIGHT 384 ? progress * mWindowScaleMarginX 385 : screenWidth - progress * mWindowScaleMarginX - width; 386 387 mCurrentRect.set(left, top, left + width, top + height); 388 float cornerRadius = Utilities.mapRange( 389 progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius); 390 applyTransform(mCurrentRect, cornerRadius); 391 392 customizeStatusBarAppearance(top > mStatusBarHeight / 2); 393 } 394 395 /** Transform the target window to match the target rect. */ applyTransform(RectF targetRect, float cornerRadius)396 private void applyTransform(RectF targetRect, float cornerRadius) { 397 final float scale = targetRect.width() / mStartRect.width(); 398 mTransformMatrix.reset(); 399 mTransformMatrix.setScale(scale, scale); 400 mTransformMatrix.postTranslate(targetRect.left, targetRect.top); 401 402 if (mBackTarget.leash.isValid()) { 403 mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]); 404 mTransaction.setWindowCrop(mBackTarget.leash, mStartRect); 405 mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius); 406 } 407 applyTransaction(); 408 } 409 applyTransaction()410 private void applyTransaction() { 411 mTransaction.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); 412 mTransaction.apply(); 413 } 414 startTransition()415 private void startTransition() { 416 if (mBackTarget == null) { 417 // Trigger transition system instead of custom transition animation. 418 finishAnimation(); 419 return; 420 } 421 if (mLauncher.isDestroyed()) { 422 return; 423 } 424 mLauncher.setPredictiveBackToHomeInProgress(true); 425 LauncherTaskbarUIController taskbarUIController = mLauncher.getTaskbarUIController(); 426 if (taskbarUIController != null) { 427 taskbarUIController.onLauncherVisibilityChanged(true); 428 } 429 // TODO: Catch the moment when launcher becomes visible after the top app un-occludes 430 // launcher and start animating afterwards. Currently we occasionally get a flicker from 431 // animating when launcher is still invisible. 432 if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) { 433 mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS); 434 mLauncher.getStateManager().moveToRestState(); 435 } 436 437 setLauncherTargetViewVisible(true); 438 439 // Explicitly close opened floating views (which is typically called from 440 // Launcher#onResumed, but in the predictive back flow launcher is not resumed until 441 // the transition is fully finished.) 442 AbstractFloatingView.closeAllOpenViewsExcept(mLauncher, false, TYPE_REBIND_SAFE); 443 float cornerRadius = Utilities.mapRange( 444 mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius); 445 final RectF resolveRectF = new RectF(); 446 mQuickstepTransitionManager.transferRectToTargetCoordinate( 447 mBackTarget, mCurrentRect, true, resolveRectF); 448 449 Pair<RectFSpringAnim, AnimatorSet> pair = 450 mQuickstepTransitionManager.createWallpaperOpenAnimations( 451 new RemoteAnimationTarget[]{mBackTarget}, 452 new RemoteAnimationTarget[0], 453 false /* fromUnlock */, 454 resolveRectF, 455 cornerRadius, 456 mBackInProgress /* fromPredictiveBack */); 457 startTransitionAnimations(pair.first, pair.second); 458 mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); 459 customizeStatusBarAppearance(true); 460 } 461 finishAnimation()462 private void finishAnimation() { 463 mLauncher.setPredictiveBackToHomeInProgress(false); 464 mBackTarget = null; 465 mLauncherTarget = null; 466 mBackInProgress = false; 467 mBackProgress = 0; 468 mTransformMatrix.reset(); 469 mCurrentRect.setEmpty(); 470 mStartRect.setEmpty(); 471 mInitialTouchPos.set(0, 0); 472 mAnimatorSetInProgress = false; 473 mSpringAnimationInProgress = false; 474 setLauncherTargetViewVisible(true); 475 mLauncherTargetView = null; 476 // We don't call customizeStatusBarAppearance here to prevent the status bar update with 477 // the legacy appearance. It should be refreshed after the transition done. 478 mOverridingStatusBarFlags = false; 479 if (mAnimationFinishedCallback != null) { 480 try { 481 mAnimationFinishedCallback.onAnimationFinished(); 482 } catch (RemoteException e) { 483 Log.w("ShellBackPreview", "Failed call onBackAnimationFinished", e); 484 } 485 mAnimationFinishedCallback = null; 486 } 487 if (mScrimAlphaAnimator != null && mScrimAlphaAnimator.isRunning()) { 488 mScrimAlphaAnimator.cancel(); 489 mScrimAlphaAnimator = null; 490 } 491 if (mScrimLayer != null) { 492 removeScrimLayer(); 493 } 494 } 495 startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim)496 private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) { 497 mAnimatorSetInProgress = anim != null; 498 mSpringAnimationInProgress = springAnim != null; 499 if (springAnim != null) { 500 springAnim.addAnimatorListener( 501 new AnimatorListenerAdapter() { 502 @Override 503 public void onAnimationEnd(Animator animation) { 504 mSpringAnimationInProgress = false; 505 tryFinishBackAnimation(); 506 } 507 } 508 ); 509 } 510 anim.addListener(new AnimatorListenerAdapter() { 511 @Override 512 public void onAnimationEnd(Animator animation) { 513 mAnimatorSetInProgress = false; 514 tryFinishBackAnimation(); 515 } 516 }); 517 if (mScrimLayer == null) { 518 // Scrim hasn't been attached yet. Let's attach it. 519 addScrimLayer(); 520 } 521 mScrimAlphaAnimator = new ValueAnimator().ofFloat(1, 0); 522 mScrimAlphaAnimator.addUpdateListener(animation -> { 523 float value = (Float) animation.getAnimatedValue(); 524 if (mScrimLayer != null && mScrimLayer.isValid()) { 525 mTransaction.setAlpha(mScrimLayer, value * mScrimAlpha); 526 applyTransaction(); 527 } 528 }); 529 mScrimAlphaAnimator.addListener(new AnimatorListenerAdapter() { 530 @Override 531 public void onAnimationEnd(Animator animation) { 532 resetScrim(); 533 } 534 }); 535 mScrimAlphaAnimator.setDuration(SCRIM_FADE_DURATION).start(); 536 anim.start(); 537 } 538 loadResources()539 private void loadResources() { 540 mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows( 541 mLauncher.getResources()) 542 ? mLauncher.getResources().getDimensionPixelSize( 543 R.dimen.swipe_back_window_corner_radius) 544 : 0; 545 mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher); 546 mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mLauncher); 547 } 548 549 /** 550 * Called when launcher is destroyed. Unregisters component callbacks to avoid memory leaks. 551 */ unregisterComponentCallbacks()552 public void unregisterComponentCallbacks() { 553 mLauncher.unregisterComponentCallbacks(mComponentCallbacks); 554 } 555 556 /** 557 * Registers component callbacks with the launcher to receive configuration change events. 558 */ registerComponentCallbacks()559 public void registerComponentCallbacks() { 560 mLauncher.registerComponentCallbacks(mComponentCallbacks); 561 } 562 563 resetScrim()564 private void resetScrim() { 565 removeScrimLayer(); 566 mScrimAlpha = 0; 567 } 568 tryFinishBackAnimation()569 private void tryFinishBackAnimation() { 570 if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) { 571 finishAnimation(); 572 } 573 } 574 customizeStatusBarAppearance(boolean overridingStatusBarFlags)575 private void customizeStatusBarAppearance(boolean overridingStatusBarFlags) { 576 if (mOverridingStatusBarFlags == overridingStatusBarFlags) { 577 return; 578 } 579 580 mOverridingStatusBarFlags = overridingStatusBarFlags; 581 final boolean isBackgroundDark = 582 (mLauncher.getWindow().getDecorView().getSystemUiVisibility() 583 & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) == 0; 584 final AppearanceRegion region = mOverridingStatusBarFlags 585 ? new AppearanceRegion(!isBackgroundDark ? APPEARANCE_LIGHT_STATUS_BARS : 0, 586 mBackTarget.windowConfiguration.getBounds()) 587 : null; 588 SystemUiProxy.INSTANCE.get(mLauncher).customizeStatusBarAppearance(region); 589 } 590 } 591