summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java215
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java91
3 files changed, 238 insertions, 95 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index d08c573736d1..cb5d1c42c7d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -83,11 +83,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
public static final boolean IS_ENABLED =
SystemProperties.getInt("persist.wm.debug.predictive_back",
SETTING_VALUE_ON) == SETTING_VALUE_ON;
- /** Flag for U animation features */
- public static boolean IS_U_ANIMATION_ENABLED =
- SystemProperties.getInt("persist.wm.debug.predictive_back_anim",
- SETTING_VALUE_ON) == SETTING_VALUE_ON;
-
public static final float FLING_MAX_LENGTH_SECONDS = 0.1f; // 100ms
public static final float FLING_SPEED_UP_FACTOR = 0.6f;
@@ -110,10 +105,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/** Tracks if an uninterruptible animation is in progress */
private boolean mPostCommitAnimationInProgress = false;
+
/** Tracks if we should start the back gesture on the next motion move event */
private boolean mShouldStartOnNextMoveEvent = false;
- /** @see #setTriggerBack(boolean) */
- private boolean mTriggerBack;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -128,6 +122,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private final ShellController mShellController;
private final ShellExecutor mShellExecutor;
private final Handler mBgHandler;
+
+ /**
+ * Tracks the current user back gesture.
+ */
+ private TouchTracker mCurrentTracker = new TouchTracker();
+
+ /**
+ * Tracks the next back gesture in case a new user gesture has started while the back animation
+ * (and navigation) associated with {@link #mCurrentTracker} have not yet finished.
+ */
+ private TouchTracker mQueuedTracker = new TouchTracker();
+
private final Runnable mAnimationTimeoutRunnable = () -> {
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...",
MAX_ANIMATION_DURATION);
@@ -138,8 +144,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@VisibleForTesting
BackAnimationAdapter mBackAnimationAdapter;
- private final TouchTracker mTouchTracker = new TouchTracker();
-
@Nullable
private IOnBackInvokedCallback mActiveCallback;
@@ -156,7 +160,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
setTriggerBack(false);
- onGestureFinished(false);
+ resetTouchTracker();
});
}
});
@@ -357,6 +361,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellBackAnimationRegistry.unregisterAnimation(type);
}
+ private TouchTracker getActiveTracker() {
+ if (mCurrentTracker.isActive()) return mCurrentTracker;
+ if (mQueuedTracker.isActive()) return mQueuedTracker;
+ return null;
+ }
+
/**
* Called when a new motion event needs to be transferred to this
* {@link BackAnimationController}
@@ -368,11 +378,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
float velocityY,
int keyAction,
@BackEvent.SwipeEdge int swipeEdge) {
- if (mPostCommitAnimationInProgress) {
+
+ TouchTracker activeTouchTracker = getActiveTracker();
+ if (activeTouchTracker != null) {
+ activeTouchTracker.update(touchX, touchY, velocityX, velocityY);
+ }
+
+ // two gestures are waiting to be processed at the moment, skip any further user touches
+ if (mCurrentTracker.isFinished() && mQueuedTracker.isFinished()) {
+ Log.d(TAG, "Ignoring MotionEvent because two gestures are already being queued.");
return;
}
- mTouchTracker.update(touchX, touchY, velocityX, velocityY);
if (keyAction == MotionEvent.ACTION_DOWN) {
if (!mBackGestureStarted) {
mShouldStartOnNextMoveEvent = true;
@@ -390,33 +407,46 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
ProtoLog.d(WM_SHELL_BACK_PREVIEW,
"Finishing gesture with event action: %d", keyAction);
if (keyAction == MotionEvent.ACTION_CANCEL) {
- mTriggerBack = false;
+ setTriggerBack(false);
}
- onGestureFinished(true);
+ onGestureFinished();
}
}
private void onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
- if (mBackGestureStarted || mBackNavigationInfo != null) {
- Log.e(TAG, "Animation is being initialized but is already started.");
- finishBackNavigation();
+ TouchTracker touchTracker;
+ if (mCurrentTracker.isInInitialState()) {
+ touchTracker = mCurrentTracker;
+ } else if (mQueuedTracker.isInInitialState()) {
+ touchTracker = mQueuedTracker;
+ } else {
+ Log.w(TAG, "Cannot start tracking new gesture with neither tracker in initial state.");
+ return;
}
-
- mTouchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
+ touchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
+ touchTracker.setState(TouchTracker.TouchTrackerState.ACTIVE);
mBackGestureStarted = true;
+ if (touchTracker == mCurrentTracker) {
+ // Only start the back navigation if no other gesture is being processed. Otherwise,
+ // the back navigation will be started once the current gesture has finished.
+ startBackNavigation(mCurrentTracker);
+ }
+ }
+
+ private void startBackNavigation(@NonNull TouchTracker touchTracker) {
try {
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
- onBackNavigationInfoReceived(mBackNavigationInfo);
+ onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
} catch (RemoteException remoteException) {
Log.e(TAG, "Failed to initAnimation", remoteException);
- finishBackNavigation();
+ finishBackNavigation(touchTracker.getTriggerBack());
}
}
- private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo) {
+ private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo,
+ @NonNull TouchTracker touchTracker) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo);
if (backNavigationInfo == null) {
Log.e(TAG, "Received BackNavigationInfo is null.");
@@ -430,7 +460,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
} else {
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
- dispatchOnBackStarted(mActiveCallback, mTouchTracker.createStartEvent(null));
+ dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
}
}
@@ -438,12 +468,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (!mBackGestureStarted || mBackNavigationInfo == null || mActiveCallback == null) {
return;
}
-
- final BackMotionEvent backEvent = mTouchTracker.createProgressEvent();
+ // Skip dispatching if the move corresponds to the queued instead of the current gesture
+ if (mQueuedTracker.isActive()) return;
+ final BackMotionEvent backEvent = mCurrentTracker.createProgressEvent();
dispatchOnBackProgressed(mActiveCallback, backEvent);
}
private void injectBackKey() {
+ Log.d(TAG, "injectBackKey");
sendBackEvent(KeyEvent.ACTION_DOWN);
sendBackEvent(KeyEvent.ACTION_UP);
}
@@ -488,7 +520,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
*
* @param callback the callback to be invoked when the animation ends.
*/
- private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback) {
+ private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback,
+ @NonNull TouchTracker touchTracker) {
if (callback == null) {
return;
}
@@ -497,12 +530,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (mBackNavigationInfo != null && mBackNavigationInfo.isAnimationCallback()) {
- final BackMotionEvent backMotionEvent = mTouchTracker.createProgressEvent();
+ final BackMotionEvent backMotionEvent = touchTracker.createProgressEvent();
if (backMotionEvent != null) {
// Constraints - absolute values
float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond();
float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond();
- float maxX = mTouchTracker.getMaxDistance(); // px
+ float maxX = touchTracker.getMaxDistance(); // px
float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px
// Current state
@@ -530,9 +563,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
animator.addUpdateListener(animation -> {
Float animatedValue = (Float) animation.getAnimatedValue();
- float progress = mTouchTracker.getProgress(animatedValue);
- final BackMotionEvent backEvent = mTouchTracker
- .createProgressEvent(progress);
+ float progress = touchTracker.getProgress(animatedValue);
+ final BackMotionEvent backEvent = touchTracker.createProgressEvent(
+ progress);
dispatchOnBackProgressed(mActiveCallback, backEvent);
});
@@ -591,27 +624,27 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
*/
public void setTriggerBack(boolean triggerBack) {
- if (mPostCommitAnimationInProgress) {
- return;
+ TouchTracker activeBackGestureInfo = getActiveTracker();
+ if (activeBackGestureInfo != null) {
+ activeBackGestureInfo.setTriggerBack(triggerBack);
}
- mTriggerBack = triggerBack;
- mTouchTracker.setTriggerBack(triggerBack);
}
private void setSwipeThresholds(
float linearDistance,
float maxDistance,
float nonLinearFactor) {
- mTouchTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
+ mCurrentTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
+ mQueuedTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
}
- private void invokeOrCancelBack() {
+ private void invokeOrCancelBack(@NonNull TouchTracker touchTracker) {
// Make a synchronized call to core before dispatch back event to client side.
// If the close transition happens before the core receives onAnimationFinished, there will
// play a second close animation for that transition.
if (mBackAnimationFinishedCallback != null) {
try {
- mBackAnimationFinishedCallback.onAnimationFinished(mTriggerBack);
+ mBackAnimationFinishedCallback.onAnimationFinished(touchTracker.getTriggerBack());
} catch (RemoteException e) {
Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e);
}
@@ -620,30 +653,30 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (mBackNavigationInfo != null) {
final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
- if (mTriggerBack) {
- dispatchOrAnimateOnBackInvoked(callback);
+ if (touchTracker.getTriggerBack()) {
+ dispatchOrAnimateOnBackInvoked(callback, touchTracker);
} else {
dispatchOnBackCancelled(callback);
}
}
- finishBackNavigation();
+ finishBackNavigation(touchTracker.getTriggerBack());
}
/**
* Called when the gesture is released, then it could start the post commit animation.
*/
- private void onGestureFinished(boolean fromTouch) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
- if (!mBackGestureStarted) {
- finishBackNavigation();
+ private void onGestureFinished() {
+ TouchTracker activeTouchTracker = getActiveTracker();
+ if (!mBackGestureStarted || activeTouchTracker == null) {
+ // This can happen when an unfinished gesture has been reset in resetTouchTracker
+ Log.d(TAG, "onGestureFinished called while no gesture is started");
return;
}
+ boolean triggerBack = activeTouchTracker.getTriggerBack();
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", triggerBack);
- if (fromTouch) {
- // Let touch reset the flag otherwise it will start a new back navigation and refresh
- // the info when received a new move event.
- mBackGestureStarted = false;
- }
+ mBackGestureStarted = false;
+ activeTouchTracker.setState(TouchTracker.TouchTrackerState.FINISHED);
if (mPostCommitAnimationInProgress) {
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation is still running");
@@ -652,11 +685,16 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (mBackNavigationInfo == null) {
// No focus window found or core are running recents animation, inject back key as
- // legacy behavior.
- if (mTriggerBack) {
+ // legacy behavior, or new back gesture was started while previous has not finished yet
+ if (!mQueuedTracker.isInInitialState()) {
+ Log.e(TAG, "mBackNavigationInfo is null AND there is another back animation in "
+ + "progress");
+ }
+ mCurrentTracker.reset();
+ if (triggerBack) {
injectBackKey();
}
- finishBackNavigation();
+ finishBackNavigation(triggerBack);
return;
}
@@ -664,7 +702,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
// Simply trigger and finish back navigation when no animator defined.
if (!shouldDispatchToAnimator()
|| mShellBackAnimationRegistry.isAnimationCancelledOrNull(backType)) {
- invokeOrCancelBack();
+ Log.d(TAG, "Trigger back without dispatching to animator.");
+ invokeOrCancelBack(mCurrentTracker);
+ mCurrentTracker.reset();
return;
} else if (mShellBackAnimationRegistry.isWaitingAnimation(backType)) {
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready.");
@@ -691,8 +731,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
// The next callback should be {@link #onBackAnimationFinished}.
- if (mTriggerBack) {
- dispatchOrAnimateOnBackInvoked(mActiveCallback);
+ if (mCurrentTracker.getTriggerBack()) {
+ dispatchOrAnimateOnBackInvoked(mActiveCallback, mCurrentTracker);
} else {
dispatchOnBackCancelled(mActiveCallback);
}
@@ -708,27 +748,64 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
mPostCommitAnimationInProgress = false;
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
+ Log.d(TAG, "BackAnimationController: onBackAnimationFinished()");
+
+ if (mCurrentTracker.isActive() || mCurrentTracker.isFinished()) {
+ // Trigger the real back.
+ invokeOrCancelBack(mCurrentTracker);
+ } else {
+ Log.d(TAG, "mCurrentBackGestureInfo was null when back animation finished");
+ }
+ resetTouchTracker();
+ }
+
+ /**
+ * Resets the TouchTracker and potentially starts a new back navigation in case one is queued
+ */
+ private void resetTouchTracker() {
+ TouchTracker temp = mCurrentTracker;
+ mCurrentTracker = mQueuedTracker;
+ temp.reset();
+ mQueuedTracker = temp;
+
+ if (mCurrentTracker.isInInitialState()) {
+ if (mBackGestureStarted) {
+ mBackGestureStarted = false;
+ dispatchOnBackCancelled(mActiveCallback);
+ finishBackNavigation(false);
+ Log.d(TAG, "resetTouchTracker -> reset an unfinished gesture");
+ } else {
+ Log.d(TAG, "resetTouchTracker -> no queued gesture");
+ }
+ return;
+ }
- // Trigger the real back.
- invokeOrCancelBack();
+ if (mCurrentTracker.isFinished() && mCurrentTracker.getTriggerBack()) {
+ Log.d(TAG, "resetTouchTracker -> start queued back navigation AND post commit "
+ + "animation");
+ injectBackKey();
+ finishBackNavigation(true);
+ mCurrentTracker.reset();
+ } else if (!mCurrentTracker.isFinished()) {
+ Log.d(TAG, "resetTouchTracker -> queued gesture not finished; do nothing");
+ } else {
+ Log.d(TAG, "resetTouchTracker -> reset queued gesture");
+ mCurrentTracker.reset();
+ }
}
/**
* This should be called after the whole back navigation is completed.
*/
@VisibleForTesting
- void finishBackNavigation() {
+ void finishBackNavigation(boolean triggerBack) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
- mShouldStartOnNextMoveEvent = false;
- mTouchTracker.reset();
mActiveCallback = null;
mShellBackAnimationRegistry.resetDefaultCrossActivity();
if (mBackNavigationInfo != null) {
- mBackNavigationInfo.onBackNavigationFinished(mTriggerBack);
+ mBackNavigationInfo.onBackNavigationFinished(triggerBack);
mBackNavigationInfo = null;
}
- mTriggerBack = false;
}
@@ -781,14 +858,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (apps.length >= 1) {
dispatchOnBackStarted(
mActiveCallback,
- mTouchTracker.createStartEvent(apps[0]));
+ mCurrentTracker.createStartEvent(apps[0]));
}
// Dispatch the first progress after animation start for
// smoothing the initial animation, instead of waiting for next
// onMove.
- final BackMotionEvent backFinish =
- mTouchTracker.createProgressEvent();
+ final BackMotionEvent backFinish = mCurrentTracker
+ .createProgressEvent();
dispatchOnBackProgressed(mActiveCallback, backFinish);
if (!mBackGestureStarted) {
// if the down -> up gesture happened before animation
@@ -808,7 +885,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
if (!mBackGestureStarted) {
- invokeOrCancelBack();
+ invokeOrCancelBack(mCurrentTracker);
}
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index a0ada39b459e..8a59a9f62425 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -52,6 +52,7 @@ class TouchTracker {
private float mStartThresholdX;
private int mSwipeEdge;
private boolean mCancelled;
+ private TouchTrackerState mState = TouchTrackerState.INITIAL;
void update(float touchX, float touchY, float velocityX, float velocityY) {
/**
@@ -76,6 +77,26 @@ class TouchTracker {
mTriggerBack = triggerBack;
}
+ boolean getTriggerBack() {
+ return mTriggerBack;
+ }
+
+ void setState(TouchTrackerState state) {
+ mState = state;
+ }
+
+ boolean isInInitialState() {
+ return mState == TouchTrackerState.INITIAL;
+ }
+
+ boolean isActive() {
+ return mState == TouchTrackerState.ACTIVE;
+ }
+
+ boolean isFinished() {
+ return mState == TouchTrackerState.FINISHED;
+ }
+
void setGestureStartLocation(float touchX, float touchY, int swipeEdge) {
mInitTouchX = touchX;
mInitTouchY = touchY;
@@ -89,6 +110,7 @@ class TouchTracker {
mStartThresholdX = 0;
mCancelled = false;
mTriggerBack = false;
+ mState = TouchTrackerState.INITIAL;
mSwipeEdge = BackEvent.EDGE_LEFT;
}
@@ -186,4 +208,9 @@ class TouchTracker {
mMaxDistance = maxDistance;
mNonLinearFactor = nonLinearFactor;
}
+
+ enum TouchTrackerState {
+ INITIAL, ACTIVE, FINISHED
+ }
+
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index d6141cfd21ba..6cf5450db9ea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -37,6 +37,7 @@ import android.app.WindowConfiguration;
import android.content.pm.ApplicationInfo;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteCallback;
@@ -44,9 +45,9 @@ import android.os.RemoteException;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContentResolver;
-import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.view.IRemoteAnimationRunner;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -58,7 +59,6 @@ import android.window.IOnBackInvokedCallback;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.ShellTestCase;
@@ -69,7 +69,6 @@ import com.android.wm.shell.sysui.ShellSharedConstants;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -86,9 +85,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
private static final String ANIMATION_ENABLED = "1";
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
- @Rule
- public TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
private ShellInit mShellInit;
@Mock
@@ -112,6 +108,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Mock
private BackAnimationBackground mAnimationBackground;
+ @Mock
+ private InputManager mInputManager;
+
private BackAnimationController mController;
private TestableContentResolver mContentResolver;
private TestableLooper mTestableLooper;
@@ -121,6 +120,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(InputManager.class, mInputManager);
mContext.getApplicationInfo().privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
mContentResolver = new TestableContentResolver(mContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -224,7 +224,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
.setPrepareRemoteAnimation(true)
.setOnBackNavigationDone(new RemoteCallback(result)));
triggerBackGesture();
- simulateRemoteAnimationStart(type);
+ simulateRemoteAnimationStart();
mShellExecutor.flushAll();
releaseBackGesture();
simulateRemoteAnimationFinished();
@@ -248,7 +248,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ simulateRemoteAnimationStart();
verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class));
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
@@ -274,7 +274,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ simulateRemoteAnimationStart();
verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class));
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
@@ -327,14 +327,14 @@ public class BackAnimationControllerTest extends ShellTestCase {
}
@Test
- public void ignoresGesture_transitionInProgress() throws RemoteException {
+ public void gestureQueued_WhenPreviousTransitionHasNotYetEnded() throws RemoteException {
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
/* enableAnimation = */ true,
/* isAnimationCallback = */ false);
triggerBackGesture();
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ simulateRemoteAnimationStart();
releaseBackGesture();
// Check that back invocation is dispatched.
@@ -344,24 +344,64 @@ public class BackAnimationControllerTest extends ShellTestCase {
reset(mAnimatorCallback);
reset(mBackAnimationRunner);
- // Verify that we prevent animation from restarting if another gestures happens before
- // the previous transition is finished.
- doMotionEvent(MotionEvent.ACTION_DOWN, 0);
+ // Verify that we prevent any interaction with the animator callback in case a new gesture
+ // starts while the current back animation has not ended, instead the gesture is queued
+ triggerBackGesture();
verifyNoMoreInteractions(mAnimatorCallback);
- // Finish back navigation.
+ // Finish previous back navigation.
simulateRemoteAnimationFinished();
- // Verify that more events from a rejected swipe cannot start animation.
+ // Verify that releasing the gesture causes back key to be injected
+ releaseBackGesture();
+ verify(mInputManager, times(2))
+ .injectInputEvent(any(KeyEvent.class), any(Integer.class));
+
+ // Verify that we start accepting gestures again once transition finishes.
+ doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- doMotionEvent(MotionEvent.ACTION_UP, 0);
+
+ simulateRemoteAnimationStart();
+ verify(mAnimatorCallback).onBackStarted(any());
+ verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void queuedFinishedGesture_RunsAfterPreviousTransitionEnded() throws RemoteException {
+ registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
+ /* enableAnimation = */ true,
+ /* isAnimationCallback = */ false);
+
+ triggerBackGesture();
+ simulateRemoteAnimationStart();
+ releaseBackGesture();
+
+ // Check that back invocation is dispatched.
+ verify(mAnimatorCallback).onBackInvoked();
+ verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
+
+ reset(mAnimatorCallback);
+ reset(mBackAnimationRunner);
+
+ // Verify that we prevent any interaction with the animator callback in case a new gesture
+ // starts while the current back animation has not ended, instead the gesture is queued
+ triggerBackGesture();
+ releaseBackGesture();
verifyNoMoreInteractions(mAnimatorCallback);
+ // Finish previous back navigation.
+ simulateRemoteAnimationFinished();
+
+ // Verify that back key press is injected after previous back navigation has ended
+ verify(mInputManager, times(2))
+ .injectInputEvent(any(KeyEvent.class), any(Integer.class));
+
// Verify that we start accepting gestures again once transition finishes.
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ simulateRemoteAnimationStart();
verify(mAnimatorCallback).onBackStarted(any());
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
}
@@ -377,7 +417,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
doNothing().when(mAnimatorCallback).onBackInvoked();
triggerBackGesture();
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ simulateRemoteAnimationStart();
mShellExecutor.flushAll();
releaseBackGesture();
@@ -388,7 +428,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ simulateRemoteAnimationStart();
verify(mAnimatorCallback).onBackStarted(any());
}
@@ -404,7 +444,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ simulateRemoteAnimationStart();
verify(mAnimatorCallback).onBackStarted(any());
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
@@ -443,7 +483,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
.setPrepareRemoteAnimation(true)
.setOnBackNavigationDone(new RemoteCallback(result)));
triggerBackGesture();
- simulateRemoteAnimationStart(type);
+ simulateRemoteAnimationStart();
mShellExecutor.flushAll();
releaseBackGesture();
@@ -527,7 +567,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(type);
+ simulateRemoteAnimationStart();
verify(callback).onBackStarted(any(BackMotionEvent.class));
verify(animationRunner).startAnimation(any(), any(), any(), any());
@@ -552,7 +592,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
/* swipeEdge */ BackEvent.EDGE_LEFT);
}
- private void simulateRemoteAnimationStart(int type) throws RemoteException {
+ private void simulateRemoteAnimationStart() throws RemoteException {
RemoteAnimationTarget animationTarget = createAnimationTarget();
RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
if (mController.mBackAnimationAdapter != null) {
@@ -564,7 +604,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
private void simulateRemoteAnimationFinished() {
mController.onBackAnimationFinished();
- mController.finishBackNavigation();
+ mController.finishBackNavigation(/*triggerBack*/ true);
}
private void registerAnimation(int type) {
@@ -590,5 +630,4 @@ public class BackAnimationControllerTest extends ShellTestCase {
mTriggerBack = result.getBoolean(KEY_TRIGGER_BACK);
}
}
- ;
}