Merge "Fix broken animation on two consecutive swipes." into tm-dev

This commit is contained in:
Shan Huang
2022-05-16 15:16:02 +00:00
committed by Android (Google) Code Review
2 changed files with 83 additions and 3 deletions

View File

@@ -67,8 +67,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
SETTING_VALUE_ON) != SETTING_VALUE_OFF; SETTING_VALUE_ON) != SETTING_VALUE_OFF;
private static final int PROGRESS_THRESHOLD = SystemProperties private static final int PROGRESS_THRESHOLD = SystemProperties
.getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1); .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
/**
* Max duration to wait for a transition to finish before accepting another gesture start
* request.
*/
private static final long MAX_TRANSITION_DURATION = 2000;
/** /**
* Location of the initial touch event of the back gesture. * Location of the initial touch event of the back gesture.
@@ -84,6 +88,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/** True when a back gesture is ongoing */ /** True when a back gesture is ongoing */
private boolean mBackGestureStarted = false; private boolean mBackGestureStarted = false;
/** Tracks if an uninterruptible transition is in progress */
private boolean mTransitionInProgress = false;
/** @see #setTriggerBack(boolean) */ /** @see #setTriggerBack(boolean) */
private boolean mTriggerBack; private boolean mTriggerBack;
@@ -96,6 +102,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private IOnBackInvokedCallback mBackToLauncherCallback; private IOnBackInvokedCallback mBackToLauncherCallback;
private float mTriggerThreshold; private float mTriggerThreshold;
private float mProgressThreshold; private float mProgressThreshold;
private final Runnable mResetTransitionRunnable = () -> {
finishAnimation();
mTransitionInProgress = false;
};
public BackAnimationController( public BackAnimationController(
@NonNull @ShellMainThread ShellExecutor shellExecutor, @NonNull @ShellMainThread ShellExecutor shellExecutor,
@@ -229,7 +239,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mBackToLauncherCallback = null; mBackToLauncherCallback = null;
} }
private void onBackToLauncherAnimationFinished() { @VisibleForTesting
void onBackToLauncherAnimationFinished() {
if (mBackNavigationInfo != null) { if (mBackNavigationInfo != null) {
IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
if (mTriggerBack) { if (mTriggerBack) {
@@ -246,6 +257,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* {@link BackAnimationController} * {@link BackAnimationController}
*/ */
public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) { public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) {
if (mTransitionInProgress) {
return;
}
if (action == MotionEvent.ACTION_MOVE) { if (action == MotionEvent.ACTION_MOVE) {
if (!mBackGestureStarted) { if (!mBackGestureStarted) {
// Let the animation initialized here to make sure the onPointerDownOutsideFocus // Let the animation initialized here to make sure the onPointerDownOutsideFocus
@@ -370,6 +384,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher
? mBackToLauncherCallback ? mBackToLauncherCallback
: mBackNavigationInfo.getOnBackInvokedCallback(); : mBackNavigationInfo.getOnBackInvokedCallback();
if (shouldDispatchToLauncher) {
startTransition();
}
if (mTriggerBack) { if (mTriggerBack) {
dispatchOnBackInvoked(targetCallback); dispatchOnBackInvoked(targetCallback);
} else { } else {
@@ -436,6 +453,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Sets to true when the back gesture has passed the triggering threshold, false otherwise. * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
*/ */
public void setTriggerBack(boolean triggerBack) { public void setTriggerBack(boolean triggerBack) {
if (mTransitionInProgress) {
return;
}
mTriggerBack = triggerBack; mTriggerBack = triggerBack;
} }
@@ -467,6 +487,23 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mTransaction.remove(screenshotSurface); mTransaction.remove(screenshotSurface);
} }
mTransaction.apply(); mTransaction.apply();
stopTransition();
backNavigationInfo.onBackNavigationFinished(triggerBack); backNavigationInfo.onBackNavigationFinished(triggerBack);
} }
private void startTransition() {
if (mTransitionInProgress) {
return;
}
mTransitionInProgress = true;
mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
}
private void stopTransition() {
if (!mTransitionInProgress) {
return;
}
mShellExecutor.removeCallbacks(mResetTransitionRunnable);
mTransitionInProgress = false;
}
} }

View File

@@ -26,8 +26,10 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.IActivityTaskManager; import android.app.IActivityTaskManager;
import android.app.WindowConfiguration; import android.app.WindowConfiguration;
@@ -180,7 +182,8 @@ public class BackAnimationControllerTest {
// b/207481538, we check that the surface is not moved for now, we can re-enable this once // b/207481538, we check that the surface is not moved for now, we can re-enable this once
// we implement the animation // we implement the animation
verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt()); verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt());
verify(mTransaction, never()).setPosition(animationTarget.leash, 100, 100); verify(mTransaction, never()).setPosition(
animationTarget.leash, 100, 100);
verify(mTransaction, atLeastOnce()).apply(); verify(mTransaction, atLeastOnce()).apply();
} }
@@ -251,6 +254,46 @@ public class BackAnimationControllerTest {
verify(mIOnBackInvokedCallback, never()).onBackInvoked(); verify(mIOnBackInvokedCallback, never()).onBackInvoked();
} }
public void ignoresGesture_transitionInProgress() throws RemoteException {
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
RemoteAnimationTarget animationTarget = createAnimationTarget();
createNavigationInfo(animationTarget, null, null,
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
triggerBackGesture();
// Check that back invocation is dispatched.
verify(mIOnBackInvokedCallback).onBackInvoked();
reset(mIOnBackInvokedCallback);
// Verify that we prevent animation from restarting if another gestures happens before
// the previous transition is finished.
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
verifyNoMoreInteractions(mIOnBackInvokedCallback);
// Verify that we start accepting gestures again once transition finishes.
mController.onBackToLauncherAnimationFinished();
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
verify(mIOnBackInvokedCallback).onBackStarted();
}
@Test
public void acceptsGesture_transitionTimeout() throws RemoteException {
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
RemoteAnimationTarget animationTarget = createAnimationTarget();
createNavigationInfo(animationTarget, null, null,
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
triggerBackGesture();
reset(mIOnBackInvokedCallback);
// Simulate transition timeout.
mShellExecutor.flushAll();
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
verify(mIOnBackInvokedCallback).onBackStarted();
}
private void doMotionEvent(int actionDown, int coordinate) { private void doMotionEvent(int actionDown, int coordinate) {
mController.onMotionEvent( mController.onMotionEvent(
MotionEvent.obtain(0, mEventTime, actionDown, coordinate, coordinate, 0), MotionEvent.obtain(0, mEventTime, actionDown, coordinate, coordinate, 0),