Loading services/core/java/com/android/server/wm/ActivityRecord.java +8 −1 Original line number Diff line number Diff line Loading @@ -658,6 +658,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO: Have a WindowContainer state for tracking exiting/deferred removal. boolean mIsExiting; // Force an app transition to be ran in the case the visibility of the app did not change. // We use this for the case of moving a Root Task to the back with multiple activities, and the // top activity enters PIP; the bottom activity's visibility stays the same, but we need to // run the transition. boolean mRequestForceTransition; boolean mEnteringAnimation; Loading Loading @@ -4199,6 +4204,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mUseTransferredAnimation) { return false; } // If it was set to true, reset the last request to force the transition. mRequestForceTransition = false; return super.applyAnimation(lp, transit, enter, isVoiceInteraction, sources); } Loading Loading @@ -4342,7 +4349,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // transition animation // * or this is an opening app and windows are being replaced (e.g. freeform window to // normal window). return isVisible() != visible || (!isVisible() && mIsExiting) return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting) || (visible && forAllWindows(WindowState::waitingForReplacement, true)); } Loading services/core/java/com/android/server/wm/ActivityStack.java +4 −6 Original line number Diff line number Diff line Loading @@ -2489,15 +2489,13 @@ class ActivityStack extends Task { return true; } ActivityRecord topActivity = getDisplayArea().topRunningActivity(); ActivityStack topStack = topActivity.getRootTask(); if (topStack != null && topStack != this && topActivity.isState(RESUMED)) { // The new top activity is already resumed, so there's a good chance that nothing will // get resumed below. So, update visibility now in case the transition is closed // prematurely. mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */, false /* deferResume */); ActivityRecord topActivity = getDisplayArea().topRunningActivity(); ActivityStack topStack = topActivity.getRootTask(); if (topStack != null && topStack != this && topActivity.isState(RESUMED)) { // Usually resuming a top activity triggers the next app transition, but nothing's got // resumed in this case, so we need to execute it explicitly. getDisplay().mDisplayContent.executeAppTransition(); Loading services/core/java/com/android/server/wm/RootWindowContainer.java +15 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY; import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; Loading Loading @@ -2156,6 +2157,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // On the other hand, ActivityRecord#onParentChanged takes care of setting the // up-to-dated pinned stack information on this newly created stack. r.reparent(stack, MAX_VALUE, reason); // In the case of this activity entering PIP due to it being moved to the back, // the old activity would have a TRANSIT_TASK_TO_BACK transition that needs to be // ran. But, since its visibility did not change (note how it was STOPPED/not // visible, and with it now at the back stack, it remains not visible), the logic to // add the transition is automatically skipped. We then add this activity manually // to the list of apps being closed, and request its transition to be ran. final ActivityRecord oldTopActivity = task.getTopMostActivity(); if (oldTopActivity != null && oldTopActivity.isState(STOPPED) && task.getDisplayContent().mAppTransition.getAppTransition() == TRANSIT_TASK_TO_BACK) { task.getDisplayContent().mClosingApps.add(oldTopActivity); oldTopActivity.mRequestForceTransition = true; } } // The intermediate windowing mode to be set on the ActivityRecord later. // This needs to happen before the re-parenting, otherwise we will always set the Loading services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,40 @@ public class AppTransitionControllerTest extends WindowTestsBase { opening, closing, false /* visible */)); } @Test public void testGetAnimationTargets_visibilityAlreadyUpdated_butForcedTransitionRequested() { // [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (closing, invisible) // +- [TaskStack2] - [Task2] - [ActivityRecord2] (opening, visible) final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent); final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1); activity1.setVisible(true); activity1.mVisibleRequested = true; activity1.mRequestForceTransition = true; final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent); final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2); activity2.setVisible(false); activity2.mVisibleRequested = false; activity2.mRequestForceTransition = true; final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); final ArraySet<ActivityRecord> closing = new ArraySet<>(); closing.add(activity2); // The visibility are already updated, but since forced transition is requested, it will // be included. WindowManagerService.sHierarchicalAnimations = false; assertEquals( new ArraySet<>(new WindowContainer[]{activity1}), AppTransitionController.getAnimationTargets( opening, closing, true /* visible */)); assertEquals( new ArraySet<>(new WindowContainer[]{activity2}), AppTransitionController.getAnimationTargets( opening, closing, false /* visible */)); } @Test public void testGetAnimationTargets_exitingBeforeTransition() { // Create another non-empty task so the animation target won't promote to task display area. Loading services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +24 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 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.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; Loading Loading @@ -148,6 +149,29 @@ public class RootActivityContainerTests extends ActivityTestsBase { ensureStackPlacement(mFullscreenStack, firstActivity); } @Test public void testMovingBottomMostStackActivityToPinnedStack() { final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) .setStack(mFullscreenStack).build(); final Task task = firstActivity.getTask(); final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(task) .setStack(mFullscreenStack).build(); mFullscreenStack.moveTaskToBack(task); // Ensure full screen stack has both tasks. ensureStackPlacement(mFullscreenStack, firstActivity, secondActivity); assertEquals(task.getTopMostActivity(), secondActivity); firstActivity.setState(STOPPED, "testMovingBottomMostStackActivityToPinnedStack"); // Move first activity to pinned stack. mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "initialMove"); assertTrue(firstActivity.mRequestForceTransition); } private static void ensureStackPlacement(ActivityStack stack, ActivityRecord... activities) { final Task task = stack.getBottomMostTask(); final ArrayList<ActivityRecord> stackActivities = new ArrayList<>(); Loading Loading
services/core/java/com/android/server/wm/ActivityRecord.java +8 −1 Original line number Diff line number Diff line Loading @@ -658,6 +658,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO: Have a WindowContainer state for tracking exiting/deferred removal. boolean mIsExiting; // Force an app transition to be ran in the case the visibility of the app did not change. // We use this for the case of moving a Root Task to the back with multiple activities, and the // top activity enters PIP; the bottom activity's visibility stays the same, but we need to // run the transition. boolean mRequestForceTransition; boolean mEnteringAnimation; Loading Loading @@ -4199,6 +4204,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mUseTransferredAnimation) { return false; } // If it was set to true, reset the last request to force the transition. mRequestForceTransition = false; return super.applyAnimation(lp, transit, enter, isVoiceInteraction, sources); } Loading Loading @@ -4342,7 +4349,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // transition animation // * or this is an opening app and windows are being replaced (e.g. freeform window to // normal window). return isVisible() != visible || (!isVisible() && mIsExiting) return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting) || (visible && forAllWindows(WindowState::waitingForReplacement, true)); } Loading
services/core/java/com/android/server/wm/ActivityStack.java +4 −6 Original line number Diff line number Diff line Loading @@ -2489,15 +2489,13 @@ class ActivityStack extends Task { return true; } ActivityRecord topActivity = getDisplayArea().topRunningActivity(); ActivityStack topStack = topActivity.getRootTask(); if (topStack != null && topStack != this && topActivity.isState(RESUMED)) { // The new top activity is already resumed, so there's a good chance that nothing will // get resumed below. So, update visibility now in case the transition is closed // prematurely. mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */, false /* deferResume */); ActivityRecord topActivity = getDisplayArea().topRunningActivity(); ActivityStack topStack = topActivity.getRootTask(); if (topStack != null && topStack != this && topActivity.isState(RESUMED)) { // Usually resuming a top activity triggers the next app transition, but nothing's got // resumed in this case, so we need to execute it explicitly. getDisplay().mDisplayContent.executeAppTransition(); Loading
services/core/java/com/android/server/wm/RootWindowContainer.java +15 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY; import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; Loading Loading @@ -2156,6 +2157,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // On the other hand, ActivityRecord#onParentChanged takes care of setting the // up-to-dated pinned stack information on this newly created stack. r.reparent(stack, MAX_VALUE, reason); // In the case of this activity entering PIP due to it being moved to the back, // the old activity would have a TRANSIT_TASK_TO_BACK transition that needs to be // ran. But, since its visibility did not change (note how it was STOPPED/not // visible, and with it now at the back stack, it remains not visible), the logic to // add the transition is automatically skipped. We then add this activity manually // to the list of apps being closed, and request its transition to be ran. final ActivityRecord oldTopActivity = task.getTopMostActivity(); if (oldTopActivity != null && oldTopActivity.isState(STOPPED) && task.getDisplayContent().mAppTransition.getAppTransition() == TRANSIT_TASK_TO_BACK) { task.getDisplayContent().mClosingApps.add(oldTopActivity); oldTopActivity.mRequestForceTransition = true; } } // The intermediate windowing mode to be set on the ActivityRecord later. // This needs to happen before the re-parenting, otherwise we will always set the Loading
services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,40 @@ public class AppTransitionControllerTest extends WindowTestsBase { opening, closing, false /* visible */)); } @Test public void testGetAnimationTargets_visibilityAlreadyUpdated_butForcedTransitionRequested() { // [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (closing, invisible) // +- [TaskStack2] - [Task2] - [ActivityRecord2] (opening, visible) final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent); final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1); activity1.setVisible(true); activity1.mVisibleRequested = true; activity1.mRequestForceTransition = true; final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent); final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2); activity2.setVisible(false); activity2.mVisibleRequested = false; activity2.mRequestForceTransition = true; final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); final ArraySet<ActivityRecord> closing = new ArraySet<>(); closing.add(activity2); // The visibility are already updated, but since forced transition is requested, it will // be included. WindowManagerService.sHierarchicalAnimations = false; assertEquals( new ArraySet<>(new WindowContainer[]{activity1}), AppTransitionController.getAnimationTargets( opening, closing, true /* visible */)); assertEquals( new ArraySet<>(new WindowContainer[]{activity2}), AppTransitionController.getAnimationTargets( opening, closing, false /* visible */)); } @Test public void testGetAnimationTargets_exitingBeforeTransition() { // Create another non-empty task so the animation target won't promote to task display area. Loading
services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +24 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 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.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; Loading Loading @@ -148,6 +149,29 @@ public class RootActivityContainerTests extends ActivityTestsBase { ensureStackPlacement(mFullscreenStack, firstActivity); } @Test public void testMovingBottomMostStackActivityToPinnedStack() { final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) .setStack(mFullscreenStack).build(); final Task task = firstActivity.getTask(); final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(task) .setStack(mFullscreenStack).build(); mFullscreenStack.moveTaskToBack(task); // Ensure full screen stack has both tasks. ensureStackPlacement(mFullscreenStack, firstActivity, secondActivity); assertEquals(task.getTopMostActivity(), secondActivity); firstActivity.setState(STOPPED, "testMovingBottomMostStackActivityToPinnedStack"); // Move first activity to pinned stack. mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "initialMove"); assertTrue(firstActivity.mRequestForceTransition); } private static void ensureStackPlacement(ActivityStack stack, ActivityRecord... activities) { final Task task = stack.getBottomMostTask(); final ArrayList<ActivityRecord> stackActivities = new ArrayList<>(); Loading