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.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -177,18 +164,35 @@ public class BubbleView extends FrameLayout {
|
||||
return mBadgedImageView.getDotPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -137,6 +137,16 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@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(
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user