Loading services/core/java/com/android/server/wm/DisplayContent.java +15 −6 Original line number Diff line number Diff line Loading @@ -1843,6 +1843,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } /** Returns {@code true} if the decided new rotation has not applied to configuration yet. */ private boolean isRotationChanging() { return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation(); } private void startFadeRotationAnimationIfNeeded() { if (isRotationChanging()) { startFadeRotationAnimation(false /* shouldDebounce */); } } /** * Starts the hide animation for the windows which will be rotated seamlessly. * Loading Loading @@ -3189,11 +3200,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Hide the windows which are not significant in rotation animation. So that the windows // don't need to block the unfreeze time. if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot() // Do not fade for freezing without rotation change. && mDisplayRotation.getRotation() != getWindowConfiguration().getRotation() && mFadeRotationAnimationController == null) { startFadeRotationAnimation(false /* shouldDebounce */); if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) { startFadeRotationAnimationIfNeeded(); } } Loading @@ -3214,6 +3222,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } if (!controller.isCollecting(this)) { controller.collect(this); startFadeRotationAnimationIfNeeded(); } return; } Loading @@ -3221,7 +3230,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp this, this, null /* remoteTransition */, displayChange); if (t != null) { mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); if (getRotation() != getWindowConfiguration().getRotation()) { if (isRotationChanging()) { mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN); controller.mTransitionMetricsReporter.associate(t, startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN)); Loading services/core/java/com/android/server/wm/FadeAnimationController.java +6 −19 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION; import android.annotation.NonNull; import android.content.Context; import android.util.ArrayMap; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; import android.view.animation.Animation; Loading @@ -38,7 +37,6 @@ import java.io.PrintWriter; public class FadeAnimationController { protected final DisplayContent mDisplayContent; protected final Context mContext; protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>(); public FadeAnimationController(DisplayContent displayContent) { mDisplayContent = displayContent; Loading Loading @@ -78,16 +76,8 @@ public class FadeAnimationController { return; } // We deferred the end of the animation when hiding the token, so we need to end it now that // it's shown again. final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> { final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken); if (runnable != null) { runnable.run(); } } : null; windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter, show /* hidden */, animationType, finishedCallback); show /* hidden */, animationType, null /* finishedCallback */); } protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec, Loading Loading @@ -135,7 +125,7 @@ public class FadeAnimationController { }; } protected class FadeAnimationAdapter extends LocalAnimationAdapter { protected static class FadeAnimationAdapter extends LocalAnimationAdapter { protected final boolean mShow; protected final WindowToken mToken; Loading @@ -149,13 +139,10 @@ public class FadeAnimationController { @Override public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { // We defer the end of the hide animation to ensure the tokens stay hidden until // we show them again. if (!mShow) { mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback); return true; } return false; // Defer the finish callback (restore leash) of the hide animation to ensure the token // stay hidden until it needs to show again. Besides, when starting the show animation, // the previous hide animation will be cancelled, so the callback can be ignored. return !mShow; } } } services/core/java/com/android/server/wm/FadeRotationAnimationController.java +86 −14 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public class FadeRotationAnimationController extends FadeAnimationController { /** Whether to use constant zero alpha animation. */ private boolean mHideImmediately; /** Whether this controller is triggered from shell transition. */ /** Whether this controller is triggered from shell transition with type CHANGE. */ private final boolean mIsChangeTransition; /** Whether the start transaction of the transition is committed (by shell). */ Loading @@ -59,21 +59,30 @@ public class FadeRotationAnimationController extends FadeAnimationController { /** The list to store the drawn tokens before the rotation animation starts. */ private ArrayList<WindowToken> mPendingShowTokens; /** It is used when the display has rotated, but some windows fade out in old rotation. */ private SeamlessRotator mRotator; private final int mOriginalRotation; private final boolean mHasScreenRotationAnimation; public FadeRotationAnimationController(DisplayContent displayContent) { super(displayContent); mService = displayContent.mWmService; mIsChangeTransition = displayContent.inTransition() && displayContent.mTransitionController.getCollectingTransitionType() == WindowManager.TRANSIT_CHANGE; mOriginalRotation = displayContent.getWindowConfiguration().getRotation(); final int transitionType = displayContent.mTransitionController.getCollectingTransitionType(); mIsChangeTransition = transitionType == WindowManager.TRANSIT_CHANGE; // Only CHANGE type (rotation animation) needs to wait for the start transaction. mIsStartTransactionCommitted = !mIsChangeTransition; mTimeoutRunnable = displayContent.getRotationAnimation() != null || mIsChangeTransition ? () -> { mTimeoutRunnable = displayContent.inTransition() ? () -> { synchronized (mService.mGlobalLock) { displayContent.finishFadeRotationAnimationIfPossible(); mService.mWindowPlacerLocked.performSurfacePlacement(); } } : null; if (mTimeoutRunnable != null) { mHasScreenRotationAnimation = displayContent.getRotationAnimation() != null || mIsChangeTransition; if (mHasScreenRotationAnimation) { // Hide the windows immediately because screen should have been covered by screenshot. mHideImmediately = true; } Loading Loading @@ -103,6 +112,19 @@ public class FadeRotationAnimationController extends FadeAnimationController { }, true /* traverseTopToBottom */); } @Override public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) { if (show) { final SurfaceControl leash = mTargetWindowTokens.remove(windowToken); if (leash != null && mRotator != null) { // The leash was unrotated by start transaction of transition. Clear the transform // to reshow the window in current rotation. mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash); } } super.fadeWindowToken(show, windowToken, animationType); } /** Applies show animation on the previously hidden window tokens. */ void show() { for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { Loading @@ -125,19 +147,23 @@ public class FadeRotationAnimationController extends FadeAnimationController { * controller is created for normal rotation. */ boolean show(WindowToken token) { if (!isTargetToken(token)) return false; if (!mIsStartTransactionCommitted) { // The fade-in animation should only start after the screenshot layer is shown by shell. // Otherwise the window will be blinking before the rotation animation starts. So store // to a pending list and animate them until the transaction is committed. if (mTargetWindowTokens.containsKey(token)) { if (mPendingShowTokens == null) { mPendingShowTokens = new ArrayList<>(); } mPendingShowTokens.add(token); return false; } if (!mHasScreenRotationAnimation && token.mTransitionController.inTransition()) { // Defer showing to onTransitionFinished(). return false; } if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) { // If the timeout runnable is null (fixed rotation), the case will be handled by show(). if (mTimeoutRunnable != null) { fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM); if (mTargetWindowTokens.isEmpty()) { mService.mH.removeCallbacks(mTimeoutRunnable); Loading Loading @@ -177,6 +203,15 @@ public class FadeRotationAnimationController extends FadeAnimationController { return mTargetWindowTokens.containsKey(token); } /** * Whether the insets animation leash should use previous position when running fade out * animation in rotated display. */ boolean shouldFreezeInsetsPosition(WindowState w) { return !mHasScreenRotationAnimation && w.mTransitionController.inTransition() && isTargetToken(w.mToken); } void setOnShowRunnable(Runnable onShowRunnable) { mOnShowRunnable = onShowRunnable; } Loading @@ -186,6 +221,22 @@ public class FadeRotationAnimationController extends FadeAnimationController { * transition starts. And associate transaction callback to consume pending animations. */ void setupStartTransaction(SurfaceControl.Transaction t) { if (!mIsChangeTransition) { // Take OPEN/CLOSE transition type as the example, the non-activity windows need to // fade out in previous rotation while display has rotated to the new rotation, so // their leashes are unrotated with the start transaction. mRotator = new SeamlessRotator(mOriginalRotation, mDisplayContent.getWindowConfiguration().getRotation(), mDisplayContent.getDisplayInfo(), false /* applyFixedTransformationHint */); for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final SurfaceControl leash = mTargetWindowTokens.valueAt(i); if (leash != null) { mRotator.applyTransform(t, leash); } } return; } // Hide the windows immediately because a screenshot layer should cover the screen. for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final SurfaceControl leash = mTargetWindowTokens.valueAt(i); Loading @@ -208,9 +259,30 @@ public class FadeRotationAnimationController extends FadeAnimationController { }); } void onTransitionFinished() { if (mIsChangeTransition) { // With screen rotation animation, the windows are always faded in when they are drawn. // Because if they are drawn fast enough, the fade animation should not be observable. return; } // For other transition types, the fade-in animation runs after the transition to make the // transition animation (e.g. launch activity) look cleaner. for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final WindowToken token = mTargetWindowTokens.keyAt(i); for (int j = token.getChildCount() - 1; j >= 0; j--) { // Only fade in the drawn windows. If the remaining windows are drawn later, // show(WindowToken) will be called to fade in them. if (token.getChildAt(j).isDrawFinishedLw()) { mDisplayContent.finishFadeRotationAnimation(token); break; } } } } @Override public Animation getFadeInAnimation() { if (mTimeoutRunnable != null) { if (mHasScreenRotationAnimation) { // Use a shorter animation so it is easier to align with screen rotation animation. return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter); } Loading services/core/java/com/android/server/wm/InsetsSourceProvider.java +8 −0 Original line number Diff line number Diff line Loading @@ -297,6 +297,14 @@ class InsetsSourceProvider { } private Point getWindowFrameSurfacePosition() { if (mControl != null) { final FadeRotationAnimationController fadeController = mWin.mDisplayContent.getFadeRotationAnimationController(); if (fadeController != null && fadeController.shouldFreezeInsetsPosition(mWin)) { // Use previous position because the fade-out animation runs in old rotation. return mControl.getSurfacePosition(); } } final Rect frame = mWin.getFrame(); final Point position = new Point(); mWin.transformFrameToSurfacePosition(frame.left, frame.top, position); Loading services/core/java/com/android/server/wm/NavBarFadeAnimationController.java +0 −8 Original line number Diff line number Diff line Loading @@ -95,14 +95,6 @@ public class NavBarFadeAnimationController extends FadeAnimationController{ } else { fadeAnim.run(); } } else { // If fade rotation animation is running and controlling the nav bar, make sure we empty // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation // finishes. final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken); if (runnable != null) { controller.setOnShowRunnable(runnable); } } } Loading Loading
services/core/java/com/android/server/wm/DisplayContent.java +15 −6 Original line number Diff line number Diff line Loading @@ -1843,6 +1843,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } /** Returns {@code true} if the decided new rotation has not applied to configuration yet. */ private boolean isRotationChanging() { return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation(); } private void startFadeRotationAnimationIfNeeded() { if (isRotationChanging()) { startFadeRotationAnimation(false /* shouldDebounce */); } } /** * Starts the hide animation for the windows which will be rotated seamlessly. * Loading Loading @@ -3189,11 +3200,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Hide the windows which are not significant in rotation animation. So that the windows // don't need to block the unfreeze time. if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot() // Do not fade for freezing without rotation change. && mDisplayRotation.getRotation() != getWindowConfiguration().getRotation() && mFadeRotationAnimationController == null) { startFadeRotationAnimation(false /* shouldDebounce */); if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) { startFadeRotationAnimationIfNeeded(); } } Loading @@ -3214,6 +3222,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } if (!controller.isCollecting(this)) { controller.collect(this); startFadeRotationAnimationIfNeeded(); } return; } Loading @@ -3221,7 +3230,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp this, this, null /* remoteTransition */, displayChange); if (t != null) { mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); if (getRotation() != getWindowConfiguration().getRotation()) { if (isRotationChanging()) { mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN); controller.mTransitionMetricsReporter.associate(t, startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN)); Loading
services/core/java/com/android/server/wm/FadeAnimationController.java +6 −19 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION; import android.annotation.NonNull; import android.content.Context; import android.util.ArrayMap; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; import android.view.animation.Animation; Loading @@ -38,7 +37,6 @@ import java.io.PrintWriter; public class FadeAnimationController { protected final DisplayContent mDisplayContent; protected final Context mContext; protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>(); public FadeAnimationController(DisplayContent displayContent) { mDisplayContent = displayContent; Loading Loading @@ -78,16 +76,8 @@ public class FadeAnimationController { return; } // We deferred the end of the animation when hiding the token, so we need to end it now that // it's shown again. final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> { final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken); if (runnable != null) { runnable.run(); } } : null; windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter, show /* hidden */, animationType, finishedCallback); show /* hidden */, animationType, null /* finishedCallback */); } protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec, Loading Loading @@ -135,7 +125,7 @@ public class FadeAnimationController { }; } protected class FadeAnimationAdapter extends LocalAnimationAdapter { protected static class FadeAnimationAdapter extends LocalAnimationAdapter { protected final boolean mShow; protected final WindowToken mToken; Loading @@ -149,13 +139,10 @@ public class FadeAnimationController { @Override public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { // We defer the end of the hide animation to ensure the tokens stay hidden until // we show them again. if (!mShow) { mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback); return true; } return false; // Defer the finish callback (restore leash) of the hide animation to ensure the token // stay hidden until it needs to show again. Besides, when starting the show animation, // the previous hide animation will be cancelled, so the callback can be ignored. return !mShow; } } }
services/core/java/com/android/server/wm/FadeRotationAnimationController.java +86 −14 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public class FadeRotationAnimationController extends FadeAnimationController { /** Whether to use constant zero alpha animation. */ private boolean mHideImmediately; /** Whether this controller is triggered from shell transition. */ /** Whether this controller is triggered from shell transition with type CHANGE. */ private final boolean mIsChangeTransition; /** Whether the start transaction of the transition is committed (by shell). */ Loading @@ -59,21 +59,30 @@ public class FadeRotationAnimationController extends FadeAnimationController { /** The list to store the drawn tokens before the rotation animation starts. */ private ArrayList<WindowToken> mPendingShowTokens; /** It is used when the display has rotated, but some windows fade out in old rotation. */ private SeamlessRotator mRotator; private final int mOriginalRotation; private final boolean mHasScreenRotationAnimation; public FadeRotationAnimationController(DisplayContent displayContent) { super(displayContent); mService = displayContent.mWmService; mIsChangeTransition = displayContent.inTransition() && displayContent.mTransitionController.getCollectingTransitionType() == WindowManager.TRANSIT_CHANGE; mOriginalRotation = displayContent.getWindowConfiguration().getRotation(); final int transitionType = displayContent.mTransitionController.getCollectingTransitionType(); mIsChangeTransition = transitionType == WindowManager.TRANSIT_CHANGE; // Only CHANGE type (rotation animation) needs to wait for the start transaction. mIsStartTransactionCommitted = !mIsChangeTransition; mTimeoutRunnable = displayContent.getRotationAnimation() != null || mIsChangeTransition ? () -> { mTimeoutRunnable = displayContent.inTransition() ? () -> { synchronized (mService.mGlobalLock) { displayContent.finishFadeRotationAnimationIfPossible(); mService.mWindowPlacerLocked.performSurfacePlacement(); } } : null; if (mTimeoutRunnable != null) { mHasScreenRotationAnimation = displayContent.getRotationAnimation() != null || mIsChangeTransition; if (mHasScreenRotationAnimation) { // Hide the windows immediately because screen should have been covered by screenshot. mHideImmediately = true; } Loading Loading @@ -103,6 +112,19 @@ public class FadeRotationAnimationController extends FadeAnimationController { }, true /* traverseTopToBottom */); } @Override public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) { if (show) { final SurfaceControl leash = mTargetWindowTokens.remove(windowToken); if (leash != null && mRotator != null) { // The leash was unrotated by start transaction of transition. Clear the transform // to reshow the window in current rotation. mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash); } } super.fadeWindowToken(show, windowToken, animationType); } /** Applies show animation on the previously hidden window tokens. */ void show() { for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { Loading @@ -125,19 +147,23 @@ public class FadeRotationAnimationController extends FadeAnimationController { * controller is created for normal rotation. */ boolean show(WindowToken token) { if (!isTargetToken(token)) return false; if (!mIsStartTransactionCommitted) { // The fade-in animation should only start after the screenshot layer is shown by shell. // Otherwise the window will be blinking before the rotation animation starts. So store // to a pending list and animate them until the transaction is committed. if (mTargetWindowTokens.containsKey(token)) { if (mPendingShowTokens == null) { mPendingShowTokens = new ArrayList<>(); } mPendingShowTokens.add(token); return false; } if (!mHasScreenRotationAnimation && token.mTransitionController.inTransition()) { // Defer showing to onTransitionFinished(). return false; } if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) { // If the timeout runnable is null (fixed rotation), the case will be handled by show(). if (mTimeoutRunnable != null) { fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM); if (mTargetWindowTokens.isEmpty()) { mService.mH.removeCallbacks(mTimeoutRunnable); Loading Loading @@ -177,6 +203,15 @@ public class FadeRotationAnimationController extends FadeAnimationController { return mTargetWindowTokens.containsKey(token); } /** * Whether the insets animation leash should use previous position when running fade out * animation in rotated display. */ boolean shouldFreezeInsetsPosition(WindowState w) { return !mHasScreenRotationAnimation && w.mTransitionController.inTransition() && isTargetToken(w.mToken); } void setOnShowRunnable(Runnable onShowRunnable) { mOnShowRunnable = onShowRunnable; } Loading @@ -186,6 +221,22 @@ public class FadeRotationAnimationController extends FadeAnimationController { * transition starts. And associate transaction callback to consume pending animations. */ void setupStartTransaction(SurfaceControl.Transaction t) { if (!mIsChangeTransition) { // Take OPEN/CLOSE transition type as the example, the non-activity windows need to // fade out in previous rotation while display has rotated to the new rotation, so // their leashes are unrotated with the start transaction. mRotator = new SeamlessRotator(mOriginalRotation, mDisplayContent.getWindowConfiguration().getRotation(), mDisplayContent.getDisplayInfo(), false /* applyFixedTransformationHint */); for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final SurfaceControl leash = mTargetWindowTokens.valueAt(i); if (leash != null) { mRotator.applyTransform(t, leash); } } return; } // Hide the windows immediately because a screenshot layer should cover the screen. for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final SurfaceControl leash = mTargetWindowTokens.valueAt(i); Loading @@ -208,9 +259,30 @@ public class FadeRotationAnimationController extends FadeAnimationController { }); } void onTransitionFinished() { if (mIsChangeTransition) { // With screen rotation animation, the windows are always faded in when they are drawn. // Because if they are drawn fast enough, the fade animation should not be observable. return; } // For other transition types, the fade-in animation runs after the transition to make the // transition animation (e.g. launch activity) look cleaner. for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final WindowToken token = mTargetWindowTokens.keyAt(i); for (int j = token.getChildCount() - 1; j >= 0; j--) { // Only fade in the drawn windows. If the remaining windows are drawn later, // show(WindowToken) will be called to fade in them. if (token.getChildAt(j).isDrawFinishedLw()) { mDisplayContent.finishFadeRotationAnimation(token); break; } } } } @Override public Animation getFadeInAnimation() { if (mTimeoutRunnable != null) { if (mHasScreenRotationAnimation) { // Use a shorter animation so it is easier to align with screen rotation animation. return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter); } Loading
services/core/java/com/android/server/wm/InsetsSourceProvider.java +8 −0 Original line number Diff line number Diff line Loading @@ -297,6 +297,14 @@ class InsetsSourceProvider { } private Point getWindowFrameSurfacePosition() { if (mControl != null) { final FadeRotationAnimationController fadeController = mWin.mDisplayContent.getFadeRotationAnimationController(); if (fadeController != null && fadeController.shouldFreezeInsetsPosition(mWin)) { // Use previous position because the fade-out animation runs in old rotation. return mControl.getSurfacePosition(); } } final Rect frame = mWin.getFrame(); final Point position = new Point(); mWin.transformFrameToSurfacePosition(frame.left, frame.top, position); Loading
services/core/java/com/android/server/wm/NavBarFadeAnimationController.java +0 −8 Original line number Diff line number Diff line Loading @@ -95,14 +95,6 @@ public class NavBarFadeAnimationController extends FadeAnimationController{ } else { fadeAnim.run(); } } else { // If fade rotation animation is running and controlling the nav bar, make sure we empty // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation // finishes. final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken); if (runnable != null) { controller.setOnShowRunnable(runnable); } } } Loading