diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java index 29791e8a4c..60615bbffe 100644 --- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java +++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java @@ -16,8 +16,8 @@ package com.android.quickstep; -import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.enableMultipleDesktops; +import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; import android.app.WindowConfiguration; import android.content.Context; @@ -36,6 +36,8 @@ import com.android.quickstep.util.TransformParams; import com.android.wm.shell.shared.GroupedTaskInfo; import com.android.wm.shell.shared.split.SplitBounds; +import kotlin.collections.CollectionsKt; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -50,8 +52,7 @@ public class RemoteTargetGluer { // This is the default number of handles to create when we don't know how many tasks are running // (e.g. if we're in split screen). Allocate extra for potential tasks overlaid, like volume. - private static final int DEFAULT_NUM_HANDLES = 4; - + private static final int DEFAULT_NUM_HANDLES = 10; private RemoteTargetHandle[] mRemoteTargetHandles; private SplitBounds mSplitBounds; @@ -60,7 +61,8 @@ public class RemoteTargetGluer { */ public RemoteTargetGluer(Context context, BaseContainerInterface sizingStrategy, RemoteAnimationTargets targets, boolean forDesktop) { - init(context, sizingStrategy, targets.apps.length, forDesktop); + mRemoteTargetHandles = createHandles(context, sizingStrategy, forDesktop, + targets.apps.length); } /** @@ -75,7 +77,8 @@ public class RemoteTargetGluer { if (groupedTaskInfo != null && groupedTaskInfo.isBaseType(GroupedTaskInfo.TYPE_DESK)) { // Allocate +1 to account for the DesktopWallpaperActivity added to the desk. int numHandles = groupedTaskInfo.getTaskInfoList().size() + 1; - init(context, sizingStrategy, numHandles, /* forDesktop = */ true); + mRemoteTargetHandles = createHandles(context, sizingStrategy, + /* forDesktop = */ true, numHandles); return; } } else { @@ -84,32 +87,35 @@ public class RemoteTargetGluer { if (visibleTasksCount > 0) { // Allocate +1 to account for the DesktopWallpaperActivity added to the desk. int numHandles = visibleTasksCount + 1; - init(context, sizingStrategy, numHandles, /* forDesktop = */ true); + mRemoteTargetHandles = createHandles(context, sizingStrategy, + /* forDesktop = */ true, numHandles); return; } } // Assume 2 handles needed for split, scale down as needed later on when we actually // get remote targets - init(context, sizingStrategy, DEFAULT_NUM_HANDLES, /* forDesktop = */ false); - } - - private void init(Context context, BaseContainerInterface sizingStrategy, int numHandles, - boolean forDesktop) { - mRemoteTargetHandles = createHandles(context, sizingStrategy, numHandles, forDesktop); + mRemoteTargetHandles = createHandles(context, sizingStrategy, /* forDesktop = */ false, + DEFAULT_NUM_HANDLES); } private RemoteTargetHandle[] createHandles(Context context, - BaseContainerInterface sizingStrategy, int numHandles, boolean forDesktop) { + BaseContainerInterface sizingStrategy, boolean forDesktop, int numHandles) { RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles]; for (int i = 0; i < numHandles; i++) { - TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy, forDesktop , i); - TransformParams transformParams = new TransformParams(); - handles[i] = new RemoteTargetHandle(tvs, transformParams); + handles[i] = createHandle(context, sizingStrategy, forDesktop, i); } return handles; } + private RemoteTargetHandle createHandle(Context context, + BaseContainerInterface sizingStrategy, boolean forDesktop, int taskIndex) { + TaskViewSimulator tvs = new TaskViewSimulator( + context, sizingStrategy, forDesktop , taskIndex); + TransformParams transformParams = new TransformParams(); + return new RemoteTargetHandle(tvs, transformParams); + } + /** * Pairs together {@link TaskViewSimulator}s and {@link TransformParams} into a * {@link RemoteTargetHandle} @@ -145,8 +151,6 @@ public class RemoteTargetGluer { * the left/top task, index 1 right/bottom. */ public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) { - resizeRemoteTargetHandles(targets); - // If we are in a true split screen case (2 apps running on screen), either: // a) mSplitBounds was already set (from the clicked GroupedTaskView) // b) A SplitBounds was passed up from shell (via AbsSwipeUpHandler) @@ -160,72 +164,78 @@ public class RemoteTargetGluer { mRemoteTargetHandles.length + " appsLength: " + targets.apps.length); if (mRemoteTargetHandles.length == 1) { + resizeRemoteTargetHandles(targets); // Single fullscreen app // If we're not in split screen, the splitIds count doesn't really matter since we // should always hit this case. - mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets); - if (targets.apps.length > 0) { - // Unclear why/when target.apps length == 0, but it sure does happen :( - mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(targets.apps[0], null); - } + setRemoteTargetHandle(targets, + targets.apps.length > 0 ? targets.apps[0] : null, + /* targetsToExclude = */ null, /* transitionInfo = */ null, + /* splitBounds = */ null, /* taskIndex = */ 0); } else if (!containsSplitTargets) { + resizeRemoteTargetHandles(targets); // Single App + Assistant for (int i = 0; i < mRemoteTargetHandles.length; i++) { - mRemoteTargetHandles[i].mTransformParams.setTargetSet(targets); - mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(targets.apps[i], null); - } - } else { - // Split apps (+ maybe assistant) - RemoteAnimationTarget topLeftTarget = targets.findTask(mSplitBounds.leftTopTaskId); - RemoteAnimationTarget bottomRightTarget = targets.findTask( - mSplitBounds.rightBottomTaskId); - if (topLeftTarget == null) { - Log.e(TAG, "topLeftTarget not found. mSplitBounds: " + mSplitBounds); - } - if (bottomRightTarget == null) { - Log.e(TAG, "bottomRightTarget not found. mSplitBounds: " + mSplitBounds); - } - List overlayTargets = Arrays.stream(targets.apps).filter( - target -> target.windowConfiguration.getWindowingMode() - != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW).toList(); - - // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude, - // vice versa - mRemoteTargetHandles[0].mTransformParams.setTargetSet( - createRemoteAnimationTargetsForTarget(targets, - Collections.singletonList(bottomRightTarget))); - mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, mSplitBounds); - - mRemoteTargetHandles[1].mTransformParams.setTargetSet( - createRemoteAnimationTargetsForTarget(targets, - Collections.singletonList(topLeftTarget))); - mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget, mSplitBounds); - - // Set the remaining overlay tasks to be their own TaskViewSimulator as fullscreen tasks - if (!overlayTargets.isEmpty()) { - ArrayList targetsToExclude = new ArrayList<>(); - targetsToExclude.add(topLeftTarget); - targetsToExclude.add(bottomRightTarget); - // Start i at 2 to account for top/left and bottom/right split handles already made - for (int i = 2; i < targets.apps.length; i++) { - if (i >= mRemoteTargetHandles.length) { - Log.e(TAG, String.format("Attempting to animate an untracked target" - + " (%d handles allocated, but %d want to animate)", - mRemoteTargetHandles.length, targets.apps.length)); - break; - } - mRemoteTargetHandles[i].mTransformParams.setTargetSet( - createRemoteAnimationTargetsForTarget(targets, targetsToExclude)); - mRemoteTargetHandles[i].mTaskViewSimulator.setPreview( - overlayTargets.get(i - 2)); - } - + setRemoteTargetHandle(targets, targets.apps[i], /* targetsToExclude = */ null, + /* transitionInfo = */ null, /* splitBounds = */ null, /* taskIndex = */ i); } + } else if (mSplitBounds != null) { + setSplitRemoteTargetHandles(targets); } return mRemoteTargetHandles; } + private void setSplitRemoteTargetHandles(RemoteAnimationTargets targets) { + // Split apps (+ maybe assistant) + final List leftTopTargetIds = mSplitBounds.leftTopTaskIds; + final List rightBottomTargetIds = mSplitBounds.rightBottomTaskIds; + if (leftTopTargetIds.isEmpty() || rightBottomTargetIds.isEmpty()) { + throw new IllegalStateException("The target ids is invalid: mSplitBounds = " + + mSplitBounds); + } + final List leftTopTargets = + CollectionsKt.mapNotNull(leftTopTargetIds, targets::findTask); + final List rightBottomTargets = + CollectionsKt.mapNotNull(rightBottomTargetIds, targets::findTask); + + final List overlayTargets = Arrays.stream(targets.apps).filter( + target -> isOverlayTarget(target, leftTopTargets, + rightBottomTargets)).toList(); + final int handleCount = leftTopTargets.size() + rightBottomTargets.size() + + overlayTargets.size(); + if (handleCount > targets.apps.length) { + throw new IllegalStateException("Attempting to animate app count:" + handleCount + + "but the total app count: " + targets.apps.length); + } + if (handleCount > mRemoteTargetHandles.length) { + throw new IllegalStateException("Attempting to animate app count:" + handleCount + + "but the max handle count: " + mRemoteTargetHandles.length); + } + if (handleCount < mRemoteTargetHandles.length) { + reduceRemoteTargetHandles(handleCount); + } + + int taskIndex = 0; + for (final RemoteAnimationTarget target : leftTopTargets) { + setRemoteTargetHandle(targets, target, rightBottomTargets, /* transitionInfo = */ null, + mSplitBounds, taskIndex++); + } + for (final RemoteAnimationTarget target : rightBottomTargets) { + setRemoteTargetHandle(targets, target, leftTopTargets, /* transitionInfo = */ null, + mSplitBounds, taskIndex++); + } + // Set the remaining overlay tasks to be their own TaskViewSimulator as fullscreen tasks + if (!overlayTargets.isEmpty()) { + List targetsToExclude = new ArrayList<>(leftTopTargets); + targetsToExclude.addAll(rightBottomTargets); + for (final RemoteAnimationTarget target : overlayTargets) { + setRemoteTargetHandle(targets, target, targetsToExclude, + /* transitionInfo = */ null, /* splitBounds = */ null, taskIndex++); + } + } + } + /** * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this creates distinct * transform params per app in {@code targets.apps} list. @@ -238,28 +248,67 @@ public class RemoteTargetGluer { RemoteAnimationTarget primaryTaskTarget = targets.apps[i]; List excludeTargets = Arrays.stream(targets.apps) .filter(target -> target.taskId != primaryTaskTarget.taskId).toList(); - mRemoteTargetHandles[i].mTransformParams.setTargetSet( - createRemoteAnimationTargetsForTarget(targets, excludeTargets)); - mRemoteTargetHandles[i].mTransformParams.setTransitionInfo(transitionInfo); - mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null); + setRemoteTargetHandle(targets, primaryTaskTarget, excludeTargets, transitionInfo, + /* splitBounds = */ null, i); } return mRemoteTargetHandles; } + private boolean isOverlayTarget(@NonNull RemoteAnimationTarget target, + @NonNull List leftTopTargets, + @NonNull List rightBottomTargets) { + return target.windowConfiguration.getWindowingMode() + != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW + && !leftTopTargets.contains(target) + && !rightBottomTargets.contains(target); + } + /** * Resize the `mRemoteTargetHandles` array since we assumed initial size, but * `targets.apps` is the ultimate source of truth here */ private void resizeRemoteTargetHandles(RemoteAnimationTargets targets) { - long appCount = Arrays.stream(targets.apps) + int handleCount = (int) Arrays.stream(targets.apps) .filter(app -> app.mode == targets.targetMode) .count(); - Log.d(TAG, "appCount: " + appCount + " handleLength: " + mRemoteTargetHandles.length); - if (appCount < mRemoteTargetHandles.length) { - Log.d(TAG, "resizing handles"); - RemoteTargetHandle[] newHandles = new RemoteTargetHandle[(int) appCount]; - System.arraycopy(mRemoteTargetHandles, 0/*src*/, newHandles, 0/*dst*/, (int) appCount); - mRemoteTargetHandles = newHandles; + Log.d(TAG, "appCount: " + handleCount + " handleLength: " + mRemoteTargetHandles.length); + if (handleCount < mRemoteTargetHandles.length) { + reduceRemoteTargetHandles(handleCount); + } + } + + /** + * Reduces the number of remote target handles to a specified count. + * The caller is responsible for ensuring that the target {@code handleCount} + * is always less than the current number of remote target handles + * ({@link #mRemoteTargetHandles}'s current size). + * + * @param handleCount The desired number of remote target handles after reduction. + * This value should be non-negative and less than the current size of the handle list. + */ + private void reduceRemoteTargetHandles(int handleCount) { + Log.d(TAG, "Reduce handles, count: " + handleCount); + RemoteTargetHandle[] newHandles = new RemoteTargetHandle[(int) handleCount]; + System.arraycopy(mRemoteTargetHandles, 0/*src*/, newHandles, 0/*dst*/, (int) handleCount); + mRemoteTargetHandles = newHandles; + } + + private void setRemoteTargetHandle(@NonNull RemoteAnimationTargets targets, + @Nullable RemoteAnimationTarget target, + @Nullable List targetsToExclude, + @Nullable TransitionInfo transitionInfo, + @Nullable SplitBounds splitBounds, int taskIndex) { + if (targetsToExclude != null) { + mRemoteTargetHandles[taskIndex].mTransformParams.setTargetSet( + createRemoteAnimationTargetsForTarget(targets, targetsToExclude)); + } else { + mRemoteTargetHandles[taskIndex].mTransformParams.setTargetSet(targets); + } + if (transitionInfo != null) { + mRemoteTargetHandles[taskIndex].mTransformParams.setTransitionInfo(transitionInfo); + } + if (target != null) { + mRemoteTargetHandles[taskIndex].mTaskViewSimulator.setPreview(target, splitBounds); } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/KeyboardQuickSwitchControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/KeyboardQuickSwitchControllerTest.kt index 9185f7e28e..922dee95d5 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/KeyboardQuickSwitchControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/KeyboardQuickSwitchControllerTest.kt @@ -313,8 +313,8 @@ class KeyboardQuickSwitchControllerTest { SplitBounds( /* leftTopBounds = */ Rect(), /* rightBottomBounds = */ Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ 1, + /* rightBottomTaskId = */ 2, /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50, ), ) diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt index da9cacedeb..35409dbd8a 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt @@ -1196,8 +1196,8 @@ class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() { SplitBounds( /* leftTopBounds = */ Rect(), /* rightBottomBounds = */ Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ 1, + /* rightBottomTaskId = */ 2, /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50, ), ) diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java index 732f332d65..5802788920 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java @@ -132,6 +132,42 @@ public abstract class AbsSwipeUpHandlerTestCase< /* taskInfo= */ mRunningTaskInfo, /* allowEnterPip= */ false); + protected final RemoteAnimationTarget mRemoteAnimationLeftTop = new RemoteAnimationTarget( + /* taskId= */ 1, + /* mode= */ RemoteAnimationTarget.MODE_CLOSING, + /* leash= */ new SurfaceControl(), + /* isTranslucent= */ false, + /* clipRect= */ null, + /* contentInsets= */ null, + /* prefixOrderIndex= */ 0, + /* position= */ null, + /* localBounds= */ null, + /* screenSpaceBounds= */ null, + new Configuration().windowConfiguration, + /* isNotInRecents= */ false, + /* startLeash= */ null, + /* startBounds= */ null, + /* taskInfo= */ mRunningTaskInfo, + /* allowEnterPip= */ false); + + protected final RemoteAnimationTarget mRemoteAnimationRightBottom = new RemoteAnimationTarget( + /* taskId= */ 2, + /* mode= */ RemoteAnimationTarget.MODE_CLOSING, + /* leash= */ new SurfaceControl(), + /* isTranslucent= */ false, + /* clipRect= */ null, + /* contentInsets= */ null, + /* prefixOrderIndex= */ 0, + /* position= */ null, + /* localBounds= */ null, + /* screenSpaceBounds= */ null, + new Configuration().windowConfiguration, + /* isNotInRecents= */ false, + /* startLeash= */ null, + /* startBounds= */ null, + /* taskInfo= */ mRunningTaskInfo, + /* allowEnterPip= */ false); + protected RecentsAnimationTargets mRecentsAnimationTargets; protected TaskAnimationManager mTaskAnimationManager; protected StateManager mStateManager; @@ -157,12 +193,12 @@ public abstract class AbsSwipeUpHandlerTestCase< extras.putParcelable(KEY_EXTRA_SPLIT_BOUNDS, new SplitBounds( /* leftTopBounds = */ new Rect(), /* rightBottomBounds = */ new Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ mRemoteAnimationLeftTop.taskId, + /* rightBottomTaskId = */ mRemoteAnimationRightBottom.taskId, /* snapPosition = */ SNAP_TO_2_50_50)); mRecentsAnimationTargets = new RecentsAnimationTargets( - new RemoteAnimationTarget[] {mRemoteAnimationTarget}, - new RemoteAnimationTarget[] {mRemoteAnimationTarget}, + new RemoteAnimationTarget[] {mRemoteAnimationLeftTop}, + new RemoteAnimationTarget[] {mRemoteAnimationRightBottom}, new RemoteAnimationTarget[] {mRemoteAnimationTarget}, /* homeContentInsets= */ new Rect(), /* minimizedHomeBounds= */ null, diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java index 1c780f0ee0..e8f67e7221 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java @@ -121,8 +121,8 @@ public class RecentTasksListTest { new RecentTaskInfo(), new RecentTaskInfo(), new SplitBounds( /* leftTopBounds = */ new Rect(), /* rightBottomBounds = */ new Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ 1, + /* rightBottomTaskId = */ 2, /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50)); when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt())) .thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos))); @@ -217,8 +217,8 @@ public class RecentTasksListTest { new SplitBounds( /* leftTopBounds = */ new Rect(), /* rightBottomBounds = */ new Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ 1, + /* rightBottomTaskId = */ 2, /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50)); when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt())) .thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos))); diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java index 92a22b2956..42c585e1cf 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java @@ -247,8 +247,8 @@ public class RecentsModelTest { new SplitTask(task1, task2, new SplitBounds( /* leftTopBounds = */ new Rect(), /* rightBottomBounds = */ new Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ 1, + /* rightBottomTaskId = */ 2, /* snapPosition = */ SplitScreenConstants.SNAP_TO_2_50_50))); return allTasks; } diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt index 8c0b984861..677477ffa0 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt @@ -60,8 +60,8 @@ class TasksRepositoryTest { SplitBounds( /* leftTopBounds = */ Rect(), /* rightBottomBounds = */ Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ 1, + /* rightBottomTaskId = */ 2, /* snapPosition = */ SNAP_TO_2_50_50, ), ),