Loading services/core/java/com/android/server/wm/ActivityRecord.java +19 −0 Original line number Diff line number Diff line Loading @@ -1389,6 +1389,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return parent != null ? parent.asTaskFragment() : null; } /** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */ private boolean shouldStartChangeTransition( @Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) { if (mWmService.mDisableTransitionAnimation || mDisplayContent == null || newParent == null || oldParent == null || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) { return false; } // Transition change for the activity moving into a TaskFragment of different bounds. return newParent.isOrganizedTaskFragment() && !newParent.getBounds().equals(oldParent.getBounds()); } @Override void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) { final TaskFragment oldParent = (TaskFragment) rawOldParent; Loading @@ -1397,6 +1411,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Task newTask = newParent != null ? newParent.getTask() : null; this.task = newTask; if (shouldStartChangeTransition(newParent, oldParent)) { initializeChangeTransition(getBounds()); } super.onParentChanged(newParent, oldParent); if (isPersistable()) { Loading Loading @@ -2607,6 +2625,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return parent != null ? parent.getOrganizedTaskFragment() : null; } @Override boolean isEmbedded() { final TaskFragment parent = getTaskFragment(); return parent != null && parent.isEmbedded(); Loading services/core/java/com/android/server/wm/AppTransitionController.java +15 −0 Original line number Diff line number Diff line Loading @@ -237,6 +237,8 @@ public class AppTransitionController { // Check if there is any override if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) { // Unfreeze the windows that were previously frozen for TaskFragment animation. unfreezeEmbeddedChangingWindows(); overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); } Loading Loading @@ -341,6 +343,9 @@ public class AppTransitionController { switch (changingType) { case TYPE_TASK: return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; case TYPE_ACTIVITY: // ActivityRecord is put in a change transition only when it is reparented // to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition. case TYPE_TASK_FRAGMENT: return TRANSIT_OLD_TASK_FRAGMENT_CHANGE; default: Loading Loading @@ -511,6 +516,16 @@ public class AppTransitionController { : null; } private void unfreezeEmbeddedChangingWindows() { final ArraySet<WindowContainer> changingContainers = mDisplayContent.mChangingContainers; for (int i = changingContainers.size() - 1; i >= 0; i--) { final WindowContainer wc = changingContainers.valueAt(i); if (wc.isEmbedded()) { wc.mSurfaceFreezer.unfreeze(wc.getSyncTransaction()); } } } /** * Overrides the pending transition with the remote animation defined by the * {@link ITaskFragmentOrganizer} if all windows in the transition are children of Loading services/core/java/com/android/server/wm/TaskFragment.java +6 −11 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.UserHandle.USER_NULL; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_NONE; Loading Loading @@ -465,7 +464,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { return this; } /** Returns {@code true} if this is a container for embedded activities or tasks. */ @Override boolean isEmbedded() { if (mIsEmbedded) { return true; Loading Loading @@ -2073,15 +2072,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { return !startBounds.equals(getBounds()); } /** * Initializes a change transition. See {@link SurfaceFreezer} for more information. */ void initializeChangeTransition(Rect startBounds) { mDisplayContent.prepareAppTransition(TRANSIT_CHANGE); mDisplayContent.mChangingContainers.add(this); mSurfaceFreezer.freeze(getSyncTransaction(), startBounds); } @Override void setSurfaceControl(SurfaceControl sc) { super.setSurfaceControl(sc); Loading Loading @@ -2163,6 +2153,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { return mTaskFragmentOrganizer != null; } /** Whether this is an organized {@link TaskFragment} and not a {@link Task}. */ final boolean isOrganizedTaskFragment() { return mTaskFragmentOrganizer != null; } /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */ void clearLastPausedActivity() { forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null); Loading services/core/java/com/android/server/wm/WindowContainer.java +28 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.os.UserHandle.USER_NULL; import static android.view.SurfaceControl.Transaction; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; Loading Loading @@ -2578,14 +2579,35 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSurfaceFreezer.unfreeze(getPendingTransaction()); } /** * Initializes a change transition. See {@link SurfaceFreezer} for more information. * * For now, this will only be called for the following cases: * 1. {@link Task} is changing windowing mode between fullscreen and freeform. * 2. {@link TaskFragment} is organized and is changing window bounds. * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. * * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case. */ void initializeChangeTransition(Rect startBounds) { mDisplayContent.prepareAppTransition(TRANSIT_CHANGE); mDisplayContent.mChangingContainers.add(this); mSurfaceFreezer.freeze(getSyncTransaction(), startBounds); } ArraySet<WindowContainer> getAnimationSources() { return mSurfaceAnimationSources; } @Override public SurfaceControl getFreezeSnapshotTarget() { // Only allow freezing if this window is in a TRANSIT_CHANGE if (!mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CHANGE) || !mDisplayContent.mChangingContainers.contains(this)) { return null; } return getSurfaceControl(); } @Override public Builder makeAnimationLeash() { Loading Loading @@ -3189,6 +3211,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return false; } /** @return {@code true} if this is a container for embedded activities or tasks. */ boolean isEmbedded() { return false; } /** * @return {@code true} if this container's surface should be shown when it is created. */ Loading services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +32 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; Loading @@ -54,6 +55,8 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.WindowManager; import android.window.ITaskFragmentOrganizer; import android.window.TaskFragmentOrganizer; import androidx.test.filters.SmallTest; Loading Loading @@ -391,6 +394,35 @@ public class AppTransitionTests extends WindowTestsBase { mDc.mAppTransition.getAnimationStyleResId(attrs)); } @Test public void testActivityRecordReparentToTaskFragment() { final ActivityRecord activity = createActivityRecord(mDc); activity.setVisibility(true); final Task task = activity.getTask(); // Add a TaskFragment of half of the Task size. final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); final ITaskFragmentOrganizer iOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer); final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) .setParentTask(task) .setOrganizer(organizer) .build(); final Rect taskBounds = new Rect(); task.getBounds(taskBounds); taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom); assertTrue(mDc.mChangingContainers.isEmpty()); assertFalse(mDc.mAppTransition.isTransitionSet()); // Schedule app transition when reparent activity to a TaskFragment of different size. activity.reparent(taskFragment, POSITION_TOP); assertTrue(mDc.mChangingContainers.contains(activity)); assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE)); } private class TestRemoteAnimationRunner implements IRemoteAnimationRunner { boolean mCancelled = false; @Override Loading Loading
services/core/java/com/android/server/wm/ActivityRecord.java +19 −0 Original line number Diff line number Diff line Loading @@ -1389,6 +1389,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return parent != null ? parent.asTaskFragment() : null; } /** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */ private boolean shouldStartChangeTransition( @Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) { if (mWmService.mDisableTransitionAnimation || mDisplayContent == null || newParent == null || oldParent == null || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) { return false; } // Transition change for the activity moving into a TaskFragment of different bounds. return newParent.isOrganizedTaskFragment() && !newParent.getBounds().equals(oldParent.getBounds()); } @Override void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) { final TaskFragment oldParent = (TaskFragment) rawOldParent; Loading @@ -1397,6 +1411,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Task newTask = newParent != null ? newParent.getTask() : null; this.task = newTask; if (shouldStartChangeTransition(newParent, oldParent)) { initializeChangeTransition(getBounds()); } super.onParentChanged(newParent, oldParent); if (isPersistable()) { Loading Loading @@ -2607,6 +2625,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return parent != null ? parent.getOrganizedTaskFragment() : null; } @Override boolean isEmbedded() { final TaskFragment parent = getTaskFragment(); return parent != null && parent.isEmbedded(); Loading
services/core/java/com/android/server/wm/AppTransitionController.java +15 −0 Original line number Diff line number Diff line Loading @@ -237,6 +237,8 @@ public class AppTransitionController { // Check if there is any override if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) { // Unfreeze the windows that were previously frozen for TaskFragment animation. unfreezeEmbeddedChangingWindows(); overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); } Loading Loading @@ -341,6 +343,9 @@ public class AppTransitionController { switch (changingType) { case TYPE_TASK: return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; case TYPE_ACTIVITY: // ActivityRecord is put in a change transition only when it is reparented // to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition. case TYPE_TASK_FRAGMENT: return TRANSIT_OLD_TASK_FRAGMENT_CHANGE; default: Loading Loading @@ -511,6 +516,16 @@ public class AppTransitionController { : null; } private void unfreezeEmbeddedChangingWindows() { final ArraySet<WindowContainer> changingContainers = mDisplayContent.mChangingContainers; for (int i = changingContainers.size() - 1; i >= 0; i--) { final WindowContainer wc = changingContainers.valueAt(i); if (wc.isEmbedded()) { wc.mSurfaceFreezer.unfreeze(wc.getSyncTransaction()); } } } /** * Overrides the pending transition with the remote animation defined by the * {@link ITaskFragmentOrganizer} if all windows in the transition are children of Loading
services/core/java/com/android/server/wm/TaskFragment.java +6 −11 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.UserHandle.USER_NULL; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_NONE; Loading Loading @@ -465,7 +464,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { return this; } /** Returns {@code true} if this is a container for embedded activities or tasks. */ @Override boolean isEmbedded() { if (mIsEmbedded) { return true; Loading Loading @@ -2073,15 +2072,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { return !startBounds.equals(getBounds()); } /** * Initializes a change transition. See {@link SurfaceFreezer} for more information. */ void initializeChangeTransition(Rect startBounds) { mDisplayContent.prepareAppTransition(TRANSIT_CHANGE); mDisplayContent.mChangingContainers.add(this); mSurfaceFreezer.freeze(getSyncTransaction(), startBounds); } @Override void setSurfaceControl(SurfaceControl sc) { super.setSurfaceControl(sc); Loading Loading @@ -2163,6 +2153,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { return mTaskFragmentOrganizer != null; } /** Whether this is an organized {@link TaskFragment} and not a {@link Task}. */ final boolean isOrganizedTaskFragment() { return mTaskFragmentOrganizer != null; } /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */ void clearLastPausedActivity() { forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null); Loading
services/core/java/com/android/server/wm/WindowContainer.java +28 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.os.UserHandle.USER_NULL; import static android.view.SurfaceControl.Transaction; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; Loading Loading @@ -2578,14 +2579,35 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSurfaceFreezer.unfreeze(getPendingTransaction()); } /** * Initializes a change transition. See {@link SurfaceFreezer} for more information. * * For now, this will only be called for the following cases: * 1. {@link Task} is changing windowing mode between fullscreen and freeform. * 2. {@link TaskFragment} is organized and is changing window bounds. * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. * * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case. */ void initializeChangeTransition(Rect startBounds) { mDisplayContent.prepareAppTransition(TRANSIT_CHANGE); mDisplayContent.mChangingContainers.add(this); mSurfaceFreezer.freeze(getSyncTransaction(), startBounds); } ArraySet<WindowContainer> getAnimationSources() { return mSurfaceAnimationSources; } @Override public SurfaceControl getFreezeSnapshotTarget() { // Only allow freezing if this window is in a TRANSIT_CHANGE if (!mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CHANGE) || !mDisplayContent.mChangingContainers.contains(this)) { return null; } return getSurfaceControl(); } @Override public Builder makeAnimationLeash() { Loading Loading @@ -3189,6 +3211,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return false; } /** @return {@code true} if this is a container for embedded activities or tasks. */ boolean isEmbedded() { return false; } /** * @return {@code true} if this container's surface should be shown when it is created. */ Loading
services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +32 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; Loading @@ -54,6 +55,8 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.WindowManager; import android.window.ITaskFragmentOrganizer; import android.window.TaskFragmentOrganizer; import androidx.test.filters.SmallTest; Loading Loading @@ -391,6 +394,35 @@ public class AppTransitionTests extends WindowTestsBase { mDc.mAppTransition.getAnimationStyleResId(attrs)); } @Test public void testActivityRecordReparentToTaskFragment() { final ActivityRecord activity = createActivityRecord(mDc); activity.setVisibility(true); final Task task = activity.getTask(); // Add a TaskFragment of half of the Task size. final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); final ITaskFragmentOrganizer iOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer); final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) .setParentTask(task) .setOrganizer(organizer) .build(); final Rect taskBounds = new Rect(); task.getBounds(taskBounds); taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom); assertTrue(mDc.mChangingContainers.isEmpty()); assertFalse(mDc.mAppTransition.isTransitionSet()); // Schedule app transition when reparent activity to a TaskFragment of different size. activity.reparent(taskFragment, POSITION_TOP); assertTrue(mDc.mChangingContainers.contains(activity)); assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE)); } private class TestRemoteAnimationRunner implements IRemoteAnimationRunner { boolean mCancelled = false; @Override Loading