Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72c7f188 authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Android (Google) Code Review
Browse files

Merge "Add some unit tests for PB transitions" into main

parents 4f67ac89 b6557fa1
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -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) {
+215 −0
Original line number Diff line number Diff line
@@ -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);