Loading services/core/java/com/android/server/wm/RecentsAnimation.java +10 −1 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, private final ActivityDisplay mDefaultDisplay; private final int mCallingPid; /** * The activity which has been launched behind. We need to remember the activity because the * target stack may have other activities, then we are able to restore the launch-behind state * for the exact activity. */ private ActivityRecord mLaunchedTargetActivity; private int mTargetActivityType; // The stack to restore the target stack behind when the animation is finished Loading Loading @@ -175,6 +181,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Mark the target activity as launch-behind to bump its visibility for the // duration of the gesture that is driven by the recents component targetActivity.mLaunchTaskBehind = true; mLaunchedTargetActivity = targetActivity; // Fetch all the surface controls and pass them to the client to get the animation // started. Cancel any existing recents animation running synchronously (do not hold the Loading Loading @@ -233,8 +240,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks, final ActivityStack targetStack = mDefaultDisplay.getStack( WINDOWING_MODE_UNDEFINED, mTargetActivityType); // Prefer to use the original target activity instead of top activity because // we may have moved another task to top (starting 3p launcher). final ActivityRecord targetActivity = targetStack != null ? targetStack.getTopActivity() ? targetStack.isInStackLocked(mLaunchedTargetActivity) : null; if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack + " targetActivity=" + targetActivity Loading services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +4 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealM import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; Loading Loading @@ -132,6 +133,9 @@ class ActivityTestsBase { mService.setWindowManager(null); mService = null; } if (sMockWindowManagerService != null) { reset(sMockWindowManagerService); } mMockTracker.close(); mMockTracker = null; Loading services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +79 −15 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; Loading @@ -28,17 +29,20 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.platform.test.annotations.Presubmit; import android.view.IRecentsAnimationRunner; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; import org.junit.Before; import org.junit.Test; Loading @@ -50,24 +54,62 @@ import org.junit.Test; @Presubmit public class RecentsAnimationTest extends ActivityTestsBase { private Context mContext = InstrumentationRegistry.getContext(); private ComponentName mRecentsComponent; private final ComponentName mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity"); private RecentsAnimationController mRecentsAnimationController; @Before public void setUp() throws Exception { mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity"); mService = new TestActivityTaskManagerService(mContext); mRecentsAnimationController = mock(RecentsAnimationController.class); doReturn(mRecentsAnimationController).when( mService.mWindowManager).getRecentsAnimationController(); doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); final RecentTasks recentTasks = mService.getRecentTasks(); spyOn(recentTasks); mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity"); doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent(); } @Test public void testSetLaunchTaskBehindOfTargetActivity() { ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack homeStack = display.getHomeStack(); // Assume the home activity support recents. ActivityRecord targetActivity = homeStack.getTopActivity(); // Put another home activity in home stack. ActivityRecord anotherHomeActivity = new ActivityBuilder(mService) .setComponent(new ComponentName(mContext.getPackageName(), "Home2")) .setCreateTask(true) .setStack(homeStack) .build(); // Start an activity on top so the recents activity can be started. new ActivityBuilder(mService) .setCreateTask(true) .build() .getActivityStack() .moveToFront("Activity start"); // Start the recents animation. RecentsAnimationCallbacks recentsAnimation = startRecentsActivity( targetActivity.getTaskRecord().getBaseIntent().getComponent(), true /* getRecentsAnimation */); // Ensure launch-behind is set for being visible. assertTrue(targetActivity.mLaunchTaskBehind); anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome"); // The current top activity is not the recents so the animation should be canceled. verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously( eq(REORDER_KEEP_IN_PLACE), any() /* reason */); // The test uses mocked RecentsAnimationController so we have to invoke the callback // manually to simulate the flow. recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */, false /* sendUserLeaveHint */); // We should restore the launch-behind of the original target activity. assertFalse(targetActivity.mLaunchTaskBehind); } @Test public void testCancelAnimationOnVisibleStackOrderChange() { ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay(); Loading @@ -93,12 +135,9 @@ public class RecentsAnimationTest extends ActivityTestsBase { .setCreateTask(true) .setStack(fullscreenStack2) .build(); doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); // Start the recents animation Intent recentsIntent = new Intent(); recentsIntent.setComponent(mRecentsComponent); mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class)); startRecentsActivity(); fullscreenStack.moveToFront("Activity start"); Loading Loading @@ -140,12 +179,9 @@ public class RecentsAnimationTest extends ActivityTestsBase { .setCreateTask(true) .setStack(fullscreenStack2) .build(); doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); // Start the recents animation Intent recentsIntent = new Intent(); recentsIntent.setComponent(mRecentsComponent); mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class)); startRecentsActivity(); fullscreenStack.remove(); Loading @@ -154,4 +190,32 @@ public class RecentsAnimationTest extends ActivityTestsBase { eq(REORDER_KEEP_IN_PLACE), any()); verify(mRecentsAnimationController, times(0)).cancelOnNextTransitionStart(); } private void startRecentsActivity() { startRecentsActivity(mRecentsComponent, false /* getRecentsAnimation */); } /** * @return non-null {@link RecentsAnimationCallbacks} if the given {@code getRecentsAnimation} * is {@code true}. */ private RecentsAnimationCallbacks startRecentsActivity(ComponentName recentsComponent, boolean getRecentsAnimation) { RecentsAnimationCallbacks[] recentsAnimation = { null }; if (getRecentsAnimation) { doAnswer(invocation -> { // The callback is actually RecentsAnimation. recentsAnimation[0] = invocation.getArgument(2); return null; }).when(mService.mWindowManager).initializeRecentsAnimation( anyInt() /* targetActivityType */, any() /* recentsAnimationRunner */, any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */); } Intent recentsIntent = new Intent(); recentsIntent.setComponent(recentsComponent); mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */, mock(IRecentsAnimationRunner.class)); return recentsAnimation[0]; } } Loading
services/core/java/com/android/server/wm/RecentsAnimation.java +10 −1 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, private final ActivityDisplay mDefaultDisplay; private final int mCallingPid; /** * The activity which has been launched behind. We need to remember the activity because the * target stack may have other activities, then we are able to restore the launch-behind state * for the exact activity. */ private ActivityRecord mLaunchedTargetActivity; private int mTargetActivityType; // The stack to restore the target stack behind when the animation is finished Loading Loading @@ -175,6 +181,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Mark the target activity as launch-behind to bump its visibility for the // duration of the gesture that is driven by the recents component targetActivity.mLaunchTaskBehind = true; mLaunchedTargetActivity = targetActivity; // Fetch all the surface controls and pass them to the client to get the animation // started. Cancel any existing recents animation running synchronously (do not hold the Loading Loading @@ -233,8 +240,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks, final ActivityStack targetStack = mDefaultDisplay.getStack( WINDOWING_MODE_UNDEFINED, mTargetActivityType); // Prefer to use the original target activity instead of top activity because // we may have moved another task to top (starting 3p launcher). final ActivityRecord targetActivity = targetStack != null ? targetStack.getTopActivity() ? targetStack.isInStackLocked(mLaunchedTargetActivity) : null; if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack + " targetActivity=" + targetActivity Loading
services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +4 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealM import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; Loading Loading @@ -132,6 +133,9 @@ class ActivityTestsBase { mService.setWindowManager(null); mService = null; } if (sMockWindowManagerService != null) { reset(sMockWindowManagerService); } mMockTracker.close(); mMockTracker = null; Loading
services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +79 −15 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; Loading @@ -28,17 +29,20 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.platform.test.annotations.Presubmit; import android.view.IRecentsAnimationRunner; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; import org.junit.Before; import org.junit.Test; Loading @@ -50,24 +54,62 @@ import org.junit.Test; @Presubmit public class RecentsAnimationTest extends ActivityTestsBase { private Context mContext = InstrumentationRegistry.getContext(); private ComponentName mRecentsComponent; private final ComponentName mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity"); private RecentsAnimationController mRecentsAnimationController; @Before public void setUp() throws Exception { mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity"); mService = new TestActivityTaskManagerService(mContext); mRecentsAnimationController = mock(RecentsAnimationController.class); doReturn(mRecentsAnimationController).when( mService.mWindowManager).getRecentsAnimationController(); doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); final RecentTasks recentTasks = mService.getRecentTasks(); spyOn(recentTasks); mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity"); doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent(); } @Test public void testSetLaunchTaskBehindOfTargetActivity() { ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack homeStack = display.getHomeStack(); // Assume the home activity support recents. ActivityRecord targetActivity = homeStack.getTopActivity(); // Put another home activity in home stack. ActivityRecord anotherHomeActivity = new ActivityBuilder(mService) .setComponent(new ComponentName(mContext.getPackageName(), "Home2")) .setCreateTask(true) .setStack(homeStack) .build(); // Start an activity on top so the recents activity can be started. new ActivityBuilder(mService) .setCreateTask(true) .build() .getActivityStack() .moveToFront("Activity start"); // Start the recents animation. RecentsAnimationCallbacks recentsAnimation = startRecentsActivity( targetActivity.getTaskRecord().getBaseIntent().getComponent(), true /* getRecentsAnimation */); // Ensure launch-behind is set for being visible. assertTrue(targetActivity.mLaunchTaskBehind); anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome"); // The current top activity is not the recents so the animation should be canceled. verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously( eq(REORDER_KEEP_IN_PLACE), any() /* reason */); // The test uses mocked RecentsAnimationController so we have to invoke the callback // manually to simulate the flow. recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */, false /* sendUserLeaveHint */); // We should restore the launch-behind of the original target activity. assertFalse(targetActivity.mLaunchTaskBehind); } @Test public void testCancelAnimationOnVisibleStackOrderChange() { ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay(); Loading @@ -93,12 +135,9 @@ public class RecentsAnimationTest extends ActivityTestsBase { .setCreateTask(true) .setStack(fullscreenStack2) .build(); doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); // Start the recents animation Intent recentsIntent = new Intent(); recentsIntent.setComponent(mRecentsComponent); mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class)); startRecentsActivity(); fullscreenStack.moveToFront("Activity start"); Loading Loading @@ -140,12 +179,9 @@ public class RecentsAnimationTest extends ActivityTestsBase { .setCreateTask(true) .setStack(fullscreenStack2) .build(); doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); // Start the recents animation Intent recentsIntent = new Intent(); recentsIntent.setComponent(mRecentsComponent); mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class)); startRecentsActivity(); fullscreenStack.remove(); Loading @@ -154,4 +190,32 @@ public class RecentsAnimationTest extends ActivityTestsBase { eq(REORDER_KEEP_IN_PLACE), any()); verify(mRecentsAnimationController, times(0)).cancelOnNextTransitionStart(); } private void startRecentsActivity() { startRecentsActivity(mRecentsComponent, false /* getRecentsAnimation */); } /** * @return non-null {@link RecentsAnimationCallbacks} if the given {@code getRecentsAnimation} * is {@code true}. */ private RecentsAnimationCallbacks startRecentsActivity(ComponentName recentsComponent, boolean getRecentsAnimation) { RecentsAnimationCallbacks[] recentsAnimation = { null }; if (getRecentsAnimation) { doAnswer(invocation -> { // The callback is actually RecentsAnimation. recentsAnimation[0] = invocation.getArgument(2); return null; }).when(mService.mWindowManager).initializeRecentsAnimation( anyInt() /* targetActivityType */, any() /* recentsAnimationRunner */, any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */); } Intent recentsIntent = new Intent(); recentsIntent.setComponent(recentsComponent); mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */, mock(IRecentsAnimationRunner.class)); return recentsAnimation[0]; } }