summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Josh Tsuji <tsuji@google.com> 2019-05-17 03:20:44 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-05-17 03:20:44 +0000
commitbc809c8d5fc68f5140d5af58dd7feb8f6deabbdc (patch)
tree0daa022e3a5d6830d046363f95295f97117a4810
parent0a51e93a9b9493d23e18426bd0e31f15d15fb8a6 (diff)
parent7aa18112b52b3e369b0c03cc95848866cda0319b (diff)
Merge changes I38958555,Ie6879de8 into qt-dev
* changes: Align bubble behavior with DND settings. Don't animate new bubbles in until the stack pos is set.
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java30
7 files changed, 202 insertions, 37 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index dcc0419ab0cf..7bfd168eea25 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,9 @@
package com.android.systemui.bubbles;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -49,6 +52,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenModeConfig;
import android.util.Log;
import android.view.Display;
import android.view.IPinnedStackController;
@@ -75,6 +79,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -140,6 +145,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Bubbles get added to the status bar view
private final StatusBarWindowController mStatusBarWindowController;
+ private final ZenModeController mZenModeController;
private StatusBarStateListener mStatusBarStateListener;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
@@ -201,17 +207,31 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Inject
public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
BubbleData data, ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider) {
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController) {
this(context, statusBarWindowController, data, null /* synchronizer */,
- configurationController, interruptionStateProvider);
+ configurationController, interruptionStateProvider, zenModeController);
}
public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider) {
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController) {
mContext = context;
mNotificationInterruptionStateProvider = interruptionStateProvider;
+ mZenModeController = zenModeController;
+ mZenModeController.addCallback(new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(int zen) {
+ updateStackViewForZenConfig();
+ }
+
+ @Override
+ public void onConfigChanged(ZenModeConfig config) {
+ updateStackViewForZenConfig();
+ }
+ });
configurationController.addCallback(this /* configurationListener */);
@@ -257,6 +277,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
}
+
+ updateStackViewForZenConfig();
}
}
@@ -560,6 +582,28 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
};
/**
+ * Updates the stack view's suppression flags from the latest config from the zen (do not
+ * disturb) controller.
+ */
+ private void updateStackViewForZenConfig() {
+ final int suppressedEffects = mZenModeController.getConfig().suppressedVisualEffects;
+ final boolean hideNotificationDotsSelected =
+ (suppressedEffects & SUPPRESSED_EFFECT_BADGE) != 0;
+ final boolean dontPopNotifsOnScreenSelected =
+ (suppressedEffects & SUPPRESSED_EFFECT_PEEK) != 0;
+ final boolean hideFromPullDownShadeSelected =
+ (suppressedEffects & SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+
+ final boolean dndEnabled = mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;
+
+ mStackView.setSuppressNewDot(
+ dndEnabled && hideNotificationDotsSelected);
+ mStackView.setSuppressFlyout(
+ dndEnabled && (dontPopNotifsOnScreenSelected || hideFromPullDownShadeSelected));
+ }
+
+ /**
+ * Lets any listeners know if bubble state has changed.
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides. Notifies any
* {@link BubbleStateChangeListener}s of visibility changes.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 4ad3a332ebe6..6391070fe45d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -285,6 +285,9 @@ public class BubbleStackView extends FrameLayout {
private BubbleDismissView mDismissContainer;
private Runnable mAfterMagnet;
+ private boolean mSuppressNewDot = false;
+ private boolean mSuppressFlyout = false;
+
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer) {
super(context);
@@ -684,6 +687,9 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.addView(bubble.iconView, 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubble.iconView, true, mClippingParameters);
+ if (bubble.iconView != null) {
+ bubble.iconView.setSuppressDot(mSuppressNewDot, false /* animate */);
+ }
animateInFlyoutForBubble(bubble);
requestUpdate();
logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
@@ -1301,6 +1307,29 @@ public class BubbleStackView extends FrameLayout {
}
}
+ /** Sets whether all bubbles in the stack should not show the 'new' dot. */
+ void setSuppressNewDot(boolean suppressNewDot) {
+ mSuppressNewDot = suppressNewDot;
+
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
+ bv.setSuppressDot(suppressNewDot, true /* animate */);
+ }
+ }
+
+ /**
+ * Sets whether the flyout should not appear, even if the notif otherwise would generate one.
+ */
+ void setSuppressFlyout(boolean suppressFlyout) {
+ mSuppressFlyout = suppressFlyout;
+ }
+
+ /**
+ * Callback to run after the flyout hides. Also called if a new flyout is shown before the
+ * previous one animates out.
+ */
+ private Runnable mAfterFlyoutHides;
+
/**
* Animates in the flyout for the given bubble, if available, and then hides it after some time.
*/
@@ -1312,22 +1341,44 @@ public class BubbleStackView extends FrameLayout {
if (updateMessage != null
&& !isExpanded()
&& !mIsExpansionAnimating
- && !mIsGestureInProgress) {
+ && !mIsGestureInProgress
+ && !mSuppressFlyout) {
if (bubble.iconView != null) {
- bubble.iconView.setSuppressDot(true /* suppressDot */, false /* animate */);
+ // Temporarily suppress the dot while the flyout is visible.
+ bubble.iconView.setSuppressDot(
+ true /* suppressDot */, false /* animate */);
+
mFlyoutDragDeltaX = 0f;
mFlyout.setAlpha(0f);
+ if (mAfterFlyoutHides != null) {
+ mAfterFlyoutHides.run();
+ }
+
+ mAfterFlyoutHides = () -> {
+ // If we're going to suppress the dot, make it visible first so it'll
+ // visibly animate away.
+ if (mSuppressNewDot) {
+ bubble.iconView.setSuppressDot(
+ false /* suppressDot */, false /* animate */);
+ }
+
+ // Reset dot suppression. If we're not suppressing due to DND, then
+ // stop suppressing it with no animation (since the flyout has
+ // transformed into the dot). If we are suppressing due to DND, animate
+ // it away.
+ bubble.iconView.setSuppressDot(
+ mSuppressNewDot /* suppressDot */,
+ mSuppressNewDot /* animate */);
+ };
+
// Post in case layout isn't complete and getWidth returns 0.
post(() -> mFlyout.showFlyout(
updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
mStackAnimationController.isStackOnLeftSide(),
- bubble.iconView.getBadgeColor(),
- () -> {
- bubble.iconView.setSuppressDot(
- false /* suppressDot */, false /* animate */);
- }));
+ bubble.iconView.getBadgeColor(), mAfterFlyoutHides));
}
+
mFlyout.removeCallbacks(mHideFlyout);
mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
@@ -1336,6 +1387,10 @@ public class BubbleStackView extends FrameLayout {
/** Hide the flyout immediately and cancel any pending hide runnables. */
private void hideFlyoutImmediate() {
+ if (mAfterFlyoutHides != null) {
+ mAfterFlyoutHides.run();
+ }
+
mFlyout.removeCallbacks(mHideFlyout);
mFlyout.hideFlyout();
}
@@ -1438,6 +1493,7 @@ public class BubbleStackView extends FrameLayout {
int bubbsCount = mBubbleContainer.getChildCount();
for (int i = 0; i < bubbsCount; i++) {
BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
+ bv.updateDotVisibility(true /* animate */);
bv.setZ((BubbleController.MAX_BUBBLES
* getResources().getDimensionPixelSize(R.dimen.bubble_elevation)) - i);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index aa32b9456cbc..6f1ed28d649e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -138,19 +138,6 @@ public class BubbleView extends FrameLayout {
updateDotVisibility(animate, null /* after */);
}
- /**
- * Changes the dot's visibility to match the bubble view's state, running the provided callback
- * after animation if requested.
- */
- void updateDotVisibility(boolean animate, Runnable after) {
- boolean showDot = getEntry().showInShadeWhenBubble() && !mSuppressDot;
-
- if (animate) {
- animateDot(showDot, after);
- } else {
- mBadgedImageView.setShowDot(showDot);
- }
- }
/**
* Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the
@@ -178,17 +165,34 @@ public class BubbleView extends FrameLayout {
}
/**
+ * Changes the dot's visibility to match the bubble view's state, running the provided callback
+ * after animation if requested.
+ */
+ private void updateDotVisibility(boolean animate, Runnable after) {
+ boolean showDot = getEntry().showInShadeWhenBubble() && !mSuppressDot;
+
+ if (animate) {
+ animateDot(showDot, after);
+ } else {
+ mBadgedImageView.setShowDot(showDot);
+ }
+ }
+
+ /**
* Animates the badge to show or hide.
*/
private void animateDot(boolean showDot, Runnable after) {
if (mBadgedImageView.isShowingDot() != showDot) {
- mBadgedImageView.setShowDot(showDot);
+ if (showDot) {
+ mBadgedImageView.setShowDot(true);
+ }
+
mBadgedImageView.clearAnimation();
mBadgedImageView.animate().setDuration(200)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setUpdateListener((valueAnimator) -> {
float fraction = valueAnimator.getAnimatedFraction();
- fraction = showDot ? fraction : 1 - fraction;
+ fraction = showDot ? fraction : 1f - fraction;
mBadgedImageView.setDotScale(fraction);
}).withEndAction(() -> {
if (!showDot) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 8529ed42cf0a..b9cdc844eef9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -86,7 +86,10 @@ public class StackAnimationController extends
* we need to keep track of it separately from the first bubble's translation in case there are
* no bubbles, or the first bubble was just added and being animated to its new position.
*/
- private PointF mStackPosition = new PointF();
+ private PointF mStackPosition = new PointF(-1, -1);
+
+ /** Whether or not the stack's start position has been set. */
+ private boolean mStackMovedToStartPosition = false;
/** The most recent position in which the stack was resting on the edge of the screen. */
private PointF mRestingStackPosition;
@@ -193,9 +196,10 @@ public class StackAnimationController extends
/** Whether the stack is on the left side of the screen. */
public boolean isStackOnLeftSide() {
- if (mLayout == null) {
+ if (mLayout == null || !isStackPositionSet()) {
return false;
}
+
float stackCenter = mStackPosition.x + mIndividualBubbleSize / 2;
float screenCenter = mLayout.getWidth() / 2;
return stackCenter < screenCenter;
@@ -630,10 +634,9 @@ public class StackAnimationController extends
@Override
void onChildAdded(View child, int index) {
if (mLayout.getChildCount() == 1) {
- // If this is the first child added, position the stack in its starting position before
- // animating in.
- moveStackToStartPosition(() -> animateInBubble(child));
- } else if (mLayout.indexOfChild(child) == 0) {
+ // If this is the first child added, position the stack in its starting position.
+ moveStackToStartPosition();
+ } else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) {
// Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble
// to the back of the stack, it'll be largely invisible so don't bother animating it in.
animateInBubble(child);
@@ -657,16 +660,21 @@ public class StackAnimationController extends
}
/** Moves the stack, without any animation, to the starting position. */
- private void moveStackToStartPosition(Runnable after) {
+ private void moveStackToStartPosition() {
// Post to ensure that the layout's width and height have been calculated.
mLayout.setVisibility(View.INVISIBLE);
mLayout.post(() -> {
+ mStackMovedToStartPosition = true;
setStackPosition(
mRestingStackPosition == null
? getDefaultStartPosition()
: mRestingStackPosition);
mLayout.setVisibility(View.VISIBLE);
- after.run();
+
+ // Animate in the top bubble now that we're visible.
+ if (mLayout.getChildCount() > 0) {
+ animateInBubble(mLayout.getChildAt(0));
+ }
});
}
@@ -718,6 +726,10 @@ public class StackAnimationController extends
getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
}
+ private boolean isStackPositionSet() {
+ return mStackMovedToStartPosition;
+ }
+
/** Animates in the given bubble. */
private void animateInBubble(View child) {
child.setTranslationY(mStackPosition.y);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 35a15167d207..b3f6f4ecdf0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -47,6 +47,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
+import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -69,6 +70,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModeController;
import org.junit.Before;
import org.junit.Test;
@@ -99,6 +101,10 @@ public class BubbleControllerTest extends SysuiTestCase {
private DozeParameters mDozeParameters;
@Mock
private ConfigurationController mConfigurationController;
+ @Mock
+ private ZenModeController mZenModeController;
+ @Mock
+ private ZenModeConfig mZenModeConfig;
private FrameLayout mStatusBarView;
@Captor
@@ -162,6 +168,9 @@ public class BubbleControllerTest extends SysuiTestCase {
when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
+ mZenModeConfig.suppressedVisualEffects = 0;
+ when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
+
TestableNotificationInterruptionStateProvider interruptionStateProvider =
new TestableNotificationInterruptionStateProvider(mContext);
interruptionStateProvider.setUpWithPresenter(
@@ -170,7 +179,8 @@ public class BubbleControllerTest extends SysuiTestCase {
mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
mBubbleData = new BubbleData(mContext);
mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
- mBubbleData, mConfigurationController, interruptionStateProvider);
+ mBubbleData, mConfigurationController, interruptionStateProvider,
+ mZenModeController);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -628,9 +638,10 @@ public class BubbleControllerTest extends SysuiTestCase {
TestableBubbleController(Context context,
StatusBarWindowController statusBarWindowController, BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider) {
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController) {
super(context, statusBarWindowController, data, Runnable::run,
- configurationController, interruptionStateProvider);
+ configurationController, interruptionStateProvider, zenModeController);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index a398fba008bb..c6acef5d4907 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -138,6 +138,16 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase {
}
@Override
+ public boolean post(Runnable action) {
+ return mMainThreadHandler.post(action);
+ }
+
+ @Override
+ public boolean postDelayed(Runnable action, long delayMillis) {
+ return mMainThreadHandler.postDelayed(action, delayMillis);
+ }
+
+ @Override
public void setController(PhysicsAnimationController controller) {
runOnMainThreadAndBlock(
() -> super.setController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index b83276bc93da..9d5c1a4ce79e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -38,6 +38,9 @@ import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.Spy;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
@@ -46,12 +49,13 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
private TestableStackController mStackController = new TestableStackController();
private int mStackOffset;
+ private Runnable mCheckStartPosSet;
@Before
public void setUp() throws Exception {
super.setUp();
- addOneMoreThanRenderLimitBubbles();
mLayout.setController(mStackController);
+ addOneMoreThanRenderLimitBubbles();
mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
}
@@ -166,6 +170,8 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
0,
new FrameLayout.LayoutParams(50, 50));
+ waitForStartPosToBeSet();
+ waitForLayoutMessageQueue();
waitForPropertyAnimations(
DynamicAnimation.TRANSLATION_X,
DynamicAnimation.TRANSLATION_Y,
@@ -293,6 +299,28 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
}
}
+ /** Waits up to 2 seconds for the initial stack position to be initialized. */
+ private void waitForStartPosToBeSet() throws InterruptedException {
+ final CountDownLatch animLatch = new CountDownLatch(1);
+
+ mCheckStartPosSet = () -> {
+ if (mStackController.getStackPosition().x >= 0) {
+ animLatch.countDown();
+ } else {
+ mMainThreadHandler.post(mCheckStartPosSet);
+ }
+ };
+
+ mMainThreadHandler.post(mCheckStartPosSet);
+
+ try {
+ animLatch.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ mMainThreadHandler.removeCallbacks(mCheckStartPosSet);
+ throw e;
+ }
+ }
+
/**
* Testable version of the stack controller that dispatches its animations on the main thread.
*/