summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java215
2 files changed, 223 insertions, 4 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 12422874ca5d..a0a9451a0391 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
@@ -146,7 +146,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private final Handler mBgHandler;
private final WindowManager mWindowManager;
private final Transitions mTransitions;
- private final BackTransitionHandler mBackTransitionHandler;
+ @VisibleForTesting
+ final BackTransitionHandler mBackTransitionHandler;
@VisibleForTesting
final Rect mTouchableArea = new Rect();
@@ -174,7 +175,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@Nullable
private IOnBackInvokedCallback mActiveCallback;
@Nullable
- private RemoteAnimationTarget[] mApps;
+ @VisibleForTesting
+ RemoteAnimationTarget[] mApps;
@VisibleForTesting
final RemoteCallback mNavigationObserver = new RemoteCallback(
@@ -1448,7 +1450,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Check whether this transition is prepare for predictive back animation, which could
* happen when core make an activity become visible.
*/
- private boolean handlePrepareTransition(
+ @VisibleForTesting
+ boolean handlePrepareTransition(
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@@ -1491,7 +1494,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Check whether this transition is triggered from back gesture commitment.
* Reparent the transition targets to animation leashes, so the animation won't be broken.
*/
- private boolean handleCloseTransition(@NonNull TransitionInfo info,
+ @VisibleForTesting
+ boolean handleCloseTransition(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
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 56fad952dc31..311b1c5ffd21 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
@@ -16,8 +16,19 @@
package com.android.wm.shell.back;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.window.BackNavigationInfo.KEY_NAVIGATION_FINISHED;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,6 +36,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -32,6 +44,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.app.ActivityManager;
import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
import android.content.pm.ApplicationInfo;
@@ -40,6 +53,7 @@ import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
@@ -51,11 +65,16 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.window.BackEvent;
import android.window.BackMotionEvent;
import android.window.BackNavigationInfo;
import android.window.IBackAnimationFinishedCallback;
import android.window.IOnBackInvokedCallback;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -128,6 +147,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
private ShellBackAnimationRegistry mShellBackAnimationRegistry;
private Rect mTouchableRegion;
+ private BackAnimationController.BackTransitionHandler mBackTransitionHandler;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -165,6 +186,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
mShellExecutor.flushAll();
mTouchableRegion = new Rect(0, 0, 100, 100);
mController.mTouchableArea.set(mTouchableRegion);
+ mBackTransitionHandler = mController.mBackTransitionHandler;
+ spyOn(mBackTransitionHandler);
}
private void createNavigationInfo(int backType,
@@ -606,6 +629,198 @@ public class BackAnimationControllerTest extends ShellTestCase {
mCrossTaskBackAnimation.getRunner());
}
+ @Test
+ public void testCloseAsExpectTransition() {
+ final int openTaskId = 1;
+ final int closeTaskId = 2;
+ mController.mApps = createAppAnimationTargets(openTaskId, closeTaskId);
+ final IBinder mockBinder = mock(IBinder.class);
+ final SurfaceControl.Transaction st = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction ft = mock(SurfaceControl.Transaction.class);
+ // Single close
+ final TransitionInfo.Change open = createAppChange(openTaskId, TRANSIT_OPEN,
+ FLAG_BACK_GESTURE_ANIMATED | FLAG_MOVED_TO_TOP);
+ final TransitionInfo.Change close = createAppChange(closeTaskId, TRANSIT_CLOSE,
+ FLAG_BACK_GESTURE_ANIMATED);
+
+ TransitionInfo tInfo = createTransitionInfo(TRANSIT_CLOSE, open, close);
+ mBackTransitionHandler.mCloseTransitionRequested = true;
+ Transitions.TransitionFinishCallback callback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handleCloseTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+
+ // PREPARE + CLOSE
+ tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+ callback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ mBackTransitionHandler.mCloseTransitionRequested = true;
+ TransitionInfo tInfo2 = createTransitionInfo(TRANSIT_CLOSE, close);
+ Transitions.TransitionFinishCallback mergeCallback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(
+ mock(IBinder.class), tInfo2, st, mock(IBinder.class), mergeCallback);
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ verify(mergeCallback).onTransitionFinished(any());
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+
+ // PREPARE contains close info
+ tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open, close);
+ callback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mCloseTransitionRequested = true;
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handleCloseTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+
+ // PREPARE then Cancel
+ tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+ callback = mock(Transitions.TransitionFinishCallback.class);
+ final TransitionRequestInfo requestInfo = new TransitionRequestInfo(
+ TRANSIT_PREPARE_BACK_NAVIGATION, null /* triggerTask */,
+ null /* remoteTransition */);
+ mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ final TransitionInfo.Change openToClose = createAppChange(openTaskId, TRANSIT_CLOSE,
+ FLAG_BACK_GESTURE_ANIMATED);
+ tInfo2 = createTransitionInfo(TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, openToClose);
+ mBackTransitionHandler.mClosePrepareTransition = mock(IBinder.class);
+ mergeCallback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(mBackTransitionHandler.mClosePrepareTransition,
+ tInfo2, st, mock(IBinder.class), mergeCallback);
+ assertTrue("Change should be consumed", tInfo2.getChanges().isEmpty());
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ }
+
+ @Test
+ public void testCancelUnexpectedTransition() {
+ final int openTaskId = 1;
+ final int closeTaskId = 2;
+ mController.mApps = createAppAnimationTargets(openTaskId, closeTaskId);
+ final IBinder mockBinder = mock(IBinder.class);
+ final SurfaceControl.Transaction st = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction ft = mock(SurfaceControl.Transaction.class);
+ final TransitionInfo.Change open = createAppChange(openTaskId, TRANSIT_OPEN,
+ FLAG_BACK_GESTURE_ANIMATED | FLAG_MOVED_TO_TOP);
+ final TransitionInfo.Change close = createAppChange(closeTaskId, TRANSIT_CLOSE,
+ FLAG_BACK_GESTURE_ANIMATED);
+
+ // Didn't trigger close transition
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+ TransitionInfo prepareInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION,
+ open, close);
+ final Transitions.TransitionFinishCallback callback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.handleRequest(mockBinder, mock(TransitionRequestInfo.class));
+ boolean canHandle = mBackTransitionHandler.startAnimation(
+ mockBinder, prepareInfo, st, ft, callback);
+ assertFalse("Should not handle transition", canHandle);
+ assertNull(mBackTransitionHandler.mOnAnimationFinishCallback);
+
+ // Didn't trigger close transition, but receive close target.
+ final TransitionRequestInfo requestInfo = new TransitionRequestInfo(
+ TRANSIT_PREPARE_BACK_NAVIGATION, null /* triggerTask */,
+ null /* remoteTransition */);
+ prepareInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+ final Transitions.TransitionFinishCallback callback2 =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+ canHandle = mBackTransitionHandler.startAnimation(mockBinder,
+ prepareInfo, st, ft, callback2);
+ assertTrue("Handle prepare transition" , canHandle);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(prepareInfo), eq(st), eq(ft), eq(callback2));
+ final TransitionInfo closeInfo = createTransitionInfo(TRANSIT_CLOSE, close);
+ Transitions.TransitionFinishCallback mergeCallback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(mock(IBinder.class), closeInfo, ft,
+ mock(IBinder.class), mergeCallback);
+ verify(callback2).onTransitionFinished(any());
+ verify(mergeCallback, never()).onTransitionFinished(any());
+
+ // Didn't trigger close transition, but contains open target.
+ final int openTaskId2 = 3;
+ final Transitions.TransitionFinishCallback callback3 =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+ canHandle = mBackTransitionHandler.startAnimation(
+ mockBinder, prepareInfo, st, ft, callback3);
+ assertTrue("Handle prepare transition" , canHandle);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(prepareInfo), eq(st), eq(ft), eq(callback3));
+ final TransitionInfo.Change open2 = createAppChange(
+ openTaskId2, TRANSIT_OPEN, FLAG_MOVED_TO_TOP);
+ final TransitionInfo openInfo = createTransitionInfo(TRANSIT_OPEN, open2, close);
+ mergeCallback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(mock(IBinder.class), openInfo, ft,
+ mock(IBinder.class), mergeCallback);
+ verify(callback3).onTransitionFinished(any());
+ verify(mergeCallback, never()).onTransitionFinished(any());
+ }
+
+ private RemoteAnimationTarget[] createAppAnimationTargets(int openTaskId, int closeTaskId) {
+ final RemoteAnimationTarget openT = createSingleAnimationTarget(openTaskId,
+ RemoteAnimationTarget.MODE_OPENING);
+ final RemoteAnimationTarget closeT = createSingleAnimationTarget(closeTaskId,
+ RemoteAnimationTarget.MODE_CLOSING);
+ return new RemoteAnimationTarget[]{openT, closeT};
+ }
+
+ private RemoteAnimationTarget createSingleAnimationTarget(int taskId, int mode) {
+ final Rect fakeR = new Rect();
+ final Point fakeP = new Point();
+ final ActivityManager.RunningTaskInfo openTaskInfo = new ActivityManager.RunningTaskInfo();
+ openTaskInfo.taskId = taskId;
+ openTaskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+ return new RemoteAnimationTarget(
+ taskId, mode, mock(SurfaceControl.class), false, fakeR, fakeR,
+ 0, fakeP, fakeR, fakeR, new WindowConfiguration(), false,
+ mock(SurfaceControl.class), fakeR, openTaskInfo, false);
+ }
+ private TransitionInfo.Change createAppChange(
+ int taskId, @TransitionInfo.TransitionMode int mode,
+ @TransitionInfo.ChangeFlags int flags) {
+ final TransitionInfo.Change change;
+ SurfaceControl.Builder b = new SurfaceControl.Builder()
+ .setName("test task");
+ if (taskId != INVALID_TASK_ID) {
+ final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+ change = new TransitionInfo.Change(
+ taskInfo.token, b.build());
+ change.setTaskInfo(taskInfo);
+ } else {
+ change = new TransitionInfo.Change(
+ null, b.build());
+
+ }
+ change.setMode(mode);
+ change.setFlags(flags);
+ return change;
+ }
+
+ private static TransitionInfo createTransitionInfo(
+ @WindowManager.TransitionType int type, TransitionInfo.Change ... changes) {
+ final TransitionInfo info = new TransitionInfo(type, 0);
+ for (int i = 0; i < changes.length; ++i) {
+ info.addChange(changes[i]);
+ }
+ return info;
+ }
+
private void verifySystemBackBehavior(int type, BackAnimationRunner animation)
throws RemoteException {
final BackAnimationRunner animationRunner = spy(animation);