diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 175611747b..40909d5b12 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -50,9 +50,9 @@ import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STA import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO; import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -1260,10 +1260,8 @@ public class QuickstepLauncher extends Launcher { /* callback= */ success -> mSplitSelectStateController.resetState(), /* freezeTaskList= */ true, groupTask.mSplitBounds == null - ? DEFAULT_SPLIT_RATIO - : groupTask.mSplitBounds.appsStackedVertically - ? groupTask.mSplitBounds.topTaskPercent - : groupTask.mSplitBounds.leftTaskPercent); + ? SNAP_TO_50_50 + : groupTask.mSplitBounds.snapPosition); } /** diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 1c74fbedc7..0a7344a3c0 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher; import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED; import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM; @@ -39,7 +40,6 @@ import com.android.quickstep.util.GroupTask; import com.android.systemui.shared.recents.model.Task; import com.android.wm.shell.recents.IRecentTasksListener; import com.android.wm.shell.util.GroupedRecentTaskInfo; -import com.android.wm.shell.util.SplitBounds; import java.io.PrintWriter; import java.util.ArrayList; @@ -293,7 +293,7 @@ public class RecentTasksList { task2.setLastSnapshotData(taskInfo2); } final SplitConfigurationOptions.SplitBounds launcherSplitBounds = - convertSplitBounds(rawTask.getSplitBounds()); + convertShellSplitBoundsToLauncher(rawTask.getSplitBounds()); allTasks.add(new GroupTask(task1, task2, launcherSplitBounds)); } @@ -313,15 +313,6 @@ public class RecentTasksList { return new DesktopTask(tasks); } - private SplitConfigurationOptions.SplitBounds convertSplitBounds( - SplitBounds shellSplitBounds) { - return shellSplitBounds == null ? - null : - new SplitConfigurationOptions.SplitBounds( - shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds, - shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId); - } - private ArrayList copyOf(ArrayList tasks) { ArrayList newTasks = new ArrayList<>(); for (int i = 0; i < tasks.size(); i++) { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index d300cc57c2..bbcea6c4cb 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -138,7 +138,8 @@ public class RecentsAnimationCallbacks implements nonAppTargets = new RemoteAnimationTarget[0]; } final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets, - wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds); + wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds, + extras); Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { ActiveGestureLog.INSTANCE.addLog( diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java index 15e1365998..67c56e39eb 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java @@ -20,6 +20,7 @@ import static android.view.RemoteAnimationTarget.MODE_CLOSING; import android.app.WindowConfiguration; import android.graphics.Rect; +import android.os.Bundle; import android.view.RemoteAnimationTarget; import com.android.quickstep.views.DesktopTaskView; @@ -35,8 +36,8 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets { public RecentsAnimationTargets(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, - Rect homeContentInsets, Rect minimizedHomeBounds) { - super(apps, wallpapers, nonApps, MODE_CLOSING); + Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) { + super(apps, wallpapers, nonApps, MODE_CLOSING, extras); this.homeContentInsets = homeContentInsets; this.minimizedHomeBounds = minimizedHomeBounds; } diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java index 80aaad0dd9..e0c7403181 100644 --- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java @@ -19,6 +19,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import android.os.Bundle; import android.view.RemoteAnimationTarget; import java.util.ArrayList; @@ -35,6 +36,7 @@ public class RemoteAnimationTargets { public final RemoteAnimationTarget[] apps; public final RemoteAnimationTarget[] wallpapers; public final RemoteAnimationTarget[] nonApps; + public final Bundle extras; public final int targetMode; public final boolean hasRecents; @@ -42,7 +44,7 @@ public class RemoteAnimationTargets { public RemoteAnimationTargets(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, - int targetMode) { + int targetMode, Bundle extras) { ArrayList filteredApps = new ArrayList<>(); boolean hasRecents = false; if (apps != null) { @@ -61,6 +63,13 @@ public class RemoteAnimationTargets { this.targetMode = targetMode; this.hasRecents = hasRecents; this.nonApps = nonApps; + this.extras = extras; + } + + public RemoteAnimationTargets(RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + int targetMode) { + this(apps, wallpapers, nonApps, targetMode, new Bundle()); } public RemoteAnimationTarget findTask(int taskId) { diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java index 84246e93c0..3af5ab7583 100644 --- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java +++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java @@ -16,7 +16,9 @@ package com.android.quickstep; -import android.app.WindowConfiguration; +import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher; +import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; + import android.content.Context; import android.graphics.Rect; import android.util.Log; @@ -24,11 +26,12 @@ import android.view.RemoteAnimationTarget; import androidx.annotation.Nullable; -import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; +import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; import com.android.quickstep.views.DesktopTaskView; +import com.android.wm.shell.util.SplitBounds; import java.util.ArrayList; import java.util.Arrays; @@ -43,7 +46,7 @@ public class RemoteTargetGluer { private static final int DEFAULT_NUM_HANDLES = 2; private RemoteTargetHandle[] mRemoteTargetHandles; - private SplitBounds mSplitBounds; + private SplitConfigurationOptions.SplitBounds mSplitBounds; /** * Use this constructor if remote targets are split-screen independent @@ -110,6 +113,16 @@ public class RemoteTargetGluer { return mRemoteTargetHandles; } + /** + * Calls {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)} with SplitBounds + * information specified. + */ + public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets, + SplitConfigurationOptions.SplitBounds splitBounds) { + mSplitBounds = splitBounds; + return assignTargetsForSplitScreen(targets); + } + /** * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this assigns the * apps in {@code targets.apps} to the {@link #mRemoteTargetHandles} with index 0 will being @@ -129,10 +142,17 @@ public class RemoteTargetGluer { mRemoteTargetHandles = newHandles; } - boolean containsSplitTargets = Arrays.stream(targets.apps) - .anyMatch(remoteAnimationTarget -> - remoteAnimationTarget.windowConfiguration.getWindowingMode() - == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); + // 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) + // If both of these are null, we are in a 1-app or 1-app-plus-assistant case. + if (mSplitBounds == null) { + SplitBounds shellSplitBounds = targets.extras.getParcelable(KEY_EXTRA_SPLIT_BOUNDS, + SplitBounds.class); + mSplitBounds = convertShellSplitBoundsToLauncher(shellSplitBounds); + } + + boolean containsSplitTargets = mSplitBounds != null; Log.d(TAG, "containsSplitTargets? " + containsSplitTargets + " handleLength: " + mRemoteTargetHandles.length + " appsLength: " + targets.apps.length); @@ -154,40 +174,12 @@ public class RemoteTargetGluer { } } else { // Split apps (+ maybe assistant) - RemoteAnimationTarget topLeftTarget = Arrays.stream(targets.apps) - .filter(remoteAnimationTarget -> - remoteAnimationTarget.windowConfiguration.getWindowingMode() - == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW) - .findFirst().get(); - - // Fetch the adjacent target for split screen. - RemoteAnimationTarget bottomRightTarget = null; - for (int i = 0; i < targets.apps.length; i++) { - final RemoteAnimationTarget target = targets.apps[i]; - if (target.windowConfiguration.getWindowingMode() != - WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW || - target == topLeftTarget) { - continue; - } - Rect topLeftBounds = getStartBounds(topLeftTarget); - Rect bounds = getStartBounds(target); - if (topLeftBounds.left > bounds.right || topLeftBounds.top > bounds.bottom) { - bottomRightTarget = topLeftTarget; - topLeftTarget = target; - break; - } else if (topLeftBounds.right < bounds.left || topLeftBounds.bottom < bounds.top) { - bottomRightTarget = target; - break; - } - } + RemoteAnimationTarget topLeftTarget = targets.findTask(mSplitBounds.leftTopTaskId); + RemoteAnimationTarget bottomRightTarget = targets.findTask( + mSplitBounds.rightBottomTaskId); // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude, // vice versa - mSplitBounds = new SplitBounds( - getStartBounds(topLeftTarget), - getStartBounds(bottomRightTarget), - topLeftTarget.taskId, - bottomRightTarget.taskId); mRemoteTargetHandles[0].mTransformParams.setTargetSet( createRemoteAnimationTargetsForTarget(targets, bottomRightTarget)); mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, @@ -291,7 +283,7 @@ public class RemoteTargetGluer { return mRemoteTargetHandles; } - public SplitBounds getSplitBounds() { + public SplitConfigurationOptions.SplitBounds getSplitBounds() { return mSplitBounds; } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 1dc777d8cd..170847d6a3 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -19,6 +19,7 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING; import static com.android.quickstep.util.LogUtils.splitFailureMessage; @@ -63,7 +64,6 @@ import com.android.internal.util.ScreenshotRequest; import com.android.internal.view.AppearanceRegion; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; -import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistUtils; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -78,6 +78,7 @@ import com.android.systemui.unfold.progress.IUnfoldTransitionListener; import com.android.wm.shell.back.IBackAnimation; import com.android.wm.shell.bubbles.IBubbles; import com.android.wm.shell.bubbles.IBubblesListener; +import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; import com.android.wm.shell.desktopmode.IDesktopMode; import com.android.wm.shell.desktopmode.IDesktopTaskListener; import com.android.wm.shell.draganddrop.IDragAndDrop; @@ -796,12 +797,12 @@ public class SystemUiProxy implements ISystemUiProxy { /** Start multiple tasks in split-screen simultaneously. */ public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2, - @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, + @StagePosition int splitPosition, @SnapPosition int snapPosition, RemoteTransition remoteTransition, InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition, - splitRatio, remoteTransition, instanceId); + snapPosition, remoteTransition, instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e); } @@ -809,12 +810,13 @@ public class SystemUiProxy implements ISystemUiProxy { } public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1, - int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, - float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) { + int taskId, Bundle options2, @StagePosition int splitPosition, + @SnapPosition int snapPosition, RemoteTransition remoteTransition, + InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2, - splitPosition, splitRatio, remoteTransition, instanceId); + splitPosition, snapPosition, remoteTransition, instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e); } @@ -824,13 +826,13 @@ public class SystemUiProxy implements ISystemUiProxy { public void startIntents(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2, - @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, + @StagePosition int splitPosition, @SnapPosition int snapPosition, RemoteTransition remoteTransition, InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1, - pendingIntent2, userId2, shortcutInfo2, options2, splitPosition, splitRatio, - remoteTransition, instanceId); + pendingIntent2, userId2, shortcutInfo2, options2, splitPosition, + snapPosition, remoteTransition, instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e); } @@ -838,12 +840,12 @@ public class SystemUiProxy implements ISystemUiProxy { } public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId, - Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, - float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) { + Bundle options2, @StagePosition int splitPosition, @SnapPosition int snapPosition, + RemoteTransition remoteTransition, InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2, - splitPosition, splitRatio, remoteTransition, instanceId); + splitPosition, snapPosition, remoteTransition, instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e); } @@ -854,12 +856,12 @@ public class SystemUiProxy implements ISystemUiProxy { * Start multiple tasks in split-screen simultaneously. */ public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2, - Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, - float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { + Bundle options2, @StagePosition int splitPosition, @SnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2, - splitPosition, splitRatio, adapter, instanceId); + splitPosition, snapPosition, adapter, instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage( "startTasksWithLegacyTransition", "RemoteException"), e); @@ -868,13 +870,13 @@ public class SystemUiProxy implements ISystemUiProxy { } public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, - Bundle options1, int taskId, Bundle options2, - @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, - RemoteAnimationAdapter adapter, InstanceId instanceId) { + Bundle options1, int taskId, Bundle options2, @StagePosition int splitPosition, + @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1, - options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId); + options1, taskId, options2, splitPosition, snapPosition, adapter, + instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage( "startIntentAndTaskWithLegacyTransition", "RemoteException"), e); @@ -883,12 +885,12 @@ public class SystemUiProxy implements ISystemUiProxy { } public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1, - int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, - float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { + int taskId, Bundle options2, @StagePosition int splitPosition, + @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1, - taskId, options2, splitPosition, splitRatio, adapter, instanceId); + taskId, options2, splitPosition, snapPosition, adapter, instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage( "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e); @@ -903,13 +905,13 @@ public class SystemUiProxy implements ISystemUiProxy { public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, - @Nullable Bundle options2, @SplitConfigurationOptions.StagePosition int sidePosition, - float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { + @Nullable Bundle options2, @StagePosition int sidePosition, + @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { if (mSystemUiProxy != null) { try { mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1, shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2, - sidePosition, splitRatio, adapter, instanceId); + sidePosition, snapPosition, adapter, instanceId); } catch (RemoteException e) { Log.w(TAG, splitFailureMessage( "startIntentsWithLegacyTransition", "RemoteException"), e); diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 97e484a71a..34965df28b 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -191,7 +191,8 @@ public final class TaskViewUtils { if (forDesktop) { remoteTargetHandles = gluer.assignTargetsForDesktop(targets); } else if (v.containsMultipleTasks()) { - remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets); + remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets, + ((GroupedTaskView) v).getSplitBoundsConfig()); } else { remoteTargetHandles = gluer.assignTargets(targets); } diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt new file mode 100644 index 0000000000..596bb47861 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.util + +import com.android.launcher3.util.SplitConfigurationOptions +import com.android.wm.shell.util.SplitBounds + +class SplitScreenUtils { + companion object { + // TODO(b/254378592): Remove these methods when the two classes are reunited + /** Converts the shell version of SplitBounds to the launcher version */ + @JvmStatic + fun convertShellSplitBoundsToLauncher( + shellSplitBounds: SplitBounds? + ): SplitConfigurationOptions.SplitBounds? { + return if (shellSplitBounds == null) { + null + } else { + SplitConfigurationOptions.SplitBounds( + shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds, + shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId, + shellSplitBounds.snapPosition + ) + } + } + + /** Converts the launcher version of SplitBounds to the shell version */ + @JvmStatic + fun convertLauncherSplitBoundsToShell( + launcherSplitBounds: SplitConfigurationOptions.SplitBounds? + ): SplitBounds? { + return if (launcherSplitBounds == null) { + null + } else { + SplitBounds( + launcherSplitBounds.leftTopBounds, + launcherSplitBounds.rightBottomBounds, + launcherSplitBounds.leftTopTaskId, + launcherSplitBounds.rightBottomTaskId, + launcherSplitBounds.snapPosition + ) + } + } + } +} \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index 16fe07dfea..c8831c7db9 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -22,7 +22,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_PENDINGINTENT; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_TASK; @@ -33,6 +32,7 @@ import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_SINGLE_TASK import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDINGINTENT; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT; import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -98,6 +98,7 @@ import com.android.quickstep.views.SplitInstructionsView; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; +import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; import com.android.wm.shell.splitscreen.ISplitSelectListener; import java.io.PrintWriter; @@ -288,11 +289,11 @@ public class SplitSelectStateController { * To be called when the both split tasks are ready to be launched. Call after launcher side * animations are complete. */ - public void launchSplitTasks(@Nullable Consumer callback) { + public void launchSplitTasks(@SnapPosition int snapPosition, + @Nullable Consumer callback) { Pair instanceIds = LogUtils.getShellShareableInstanceId(); - launchTasks(callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO, - instanceIds.first); + launchTasks(callback, false /* freezeTaskList */, snapPosition, instanceIds.first); mStatsLogManager.logger() .withItemInfo(mSplitSelectDataHolder.getItemInfo()) @@ -301,11 +302,18 @@ public class SplitSelectStateController { } /** - * A version of {@link #launchTasks(Consumer, boolean, float, InstanceId)} with no success - * callback. + * A version of {@link #launchSplitTasks(int, Consumer)} that launches with default split ratio. + */ + public void launchSplitTasks(@Nullable Consumer callback) { + launchSplitTasks(SNAP_TO_50_50, callback); + } + + /** + * A version of {@link #launchSplitTasks(int, Consumer)} that launches with a default split + * ratio and no callback. */ public void launchSplitTasks() { - launchSplitTasks(null); + launchSplitTasks(SNAP_TO_50_50, null); } /** @@ -342,7 +350,7 @@ public class SplitSelectStateController { * foreground (quickswitch, launching previous pairs from overview) */ public void launchTasks(@Nullable Consumer callback, boolean freezeTaskList, - float splitRatio, @Nullable InstanceId shellInstanceId) { + @SnapPosition int snapPosition, @Nullable InstanceId shellInstanceId) { TestLogging.recordEvent( TestProtocol.SEQUENCE_MAIN, "launchSplitTasks"); final ActivityOptions options1 = ActivityOptions.makeBasic(); @@ -369,33 +377,33 @@ public class SplitSelectStateController { switch (launchData.getSplitLaunchType()) { case SPLIT_TASK_TASK -> mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, - null /* options2 */, initialStagePosition, splitRatio, + null /* options2 */, initialStagePosition, snapPosition, remoteTransition, shellInstanceId); case SPLIT_TASK_PENDINGINTENT -> mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle, - firstTaskId, null /*options2*/, initialStagePosition, splitRatio, + firstTaskId, null /*options2*/, initialStagePosition, snapPosition, remoteTransition, shellInstanceId); case SPLIT_TASK_SHORTCUT -> mSystemUiProxy.startShortcutAndTask(secondShortcut, optionsBundle, - firstTaskId, null /*options2*/, initialStagePosition, splitRatio, + firstTaskId, null /*options2*/, initialStagePosition, snapPosition, remoteTransition, shellInstanceId); case SPLIT_PENDINGINTENT_TASK -> mSystemUiProxy.startIntentAndTask(firstPI, firstUserId, optionsBundle, - secondTaskId, null /*options2*/, initialStagePosition, splitRatio, + secondTaskId, null /*options2*/, initialStagePosition, snapPosition, remoteTransition, shellInstanceId); case SPLIT_PENDINGINTENT_PENDINGINTENT -> mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut, optionsBundle, secondPI, secondUserId, secondShortcut, - null /*options2*/, initialStagePosition, splitRatio, + null /*options2*/, initialStagePosition, snapPosition, remoteTransition, shellInstanceId); case SPLIT_SHORTCUT_TASK -> mSystemUiProxy.startShortcutAndTask(firstShortcut, optionsBundle, - secondTaskId, null /*options2*/, initialStagePosition, splitRatio, + secondTaskId, null /*options2*/, initialStagePosition, snapPosition, remoteTransition, shellInstanceId); } } else { @@ -405,40 +413,40 @@ public class SplitSelectStateController { case SPLIT_TASK_TASK -> mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle, secondTaskId, null /* options2 */, initialStagePosition, - splitRatio, adapter, shellInstanceId); + snapPosition, adapter, shellInstanceId); case SPLIT_TASK_PENDINGINTENT -> mSystemUiProxy.startIntentAndTaskWithLegacyTransition(secondPI, secondUserId, optionsBundle, firstTaskId, null /*options2*/, - initialStagePosition, splitRatio, adapter, shellInstanceId); + initialStagePosition, snapPosition, adapter, shellInstanceId); case SPLIT_TASK_SHORTCUT -> mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(secondShortcut, optionsBundle, firstTaskId, null /*options2*/, initialStagePosition, - splitRatio, adapter, shellInstanceId); + snapPosition, adapter, shellInstanceId); case SPLIT_PENDINGINTENT_TASK -> mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI, firstUserId, optionsBundle, secondTaskId, null /*options2*/, - initialStagePosition, splitRatio, adapter, shellInstanceId); + initialStagePosition, snapPosition, adapter, shellInstanceId); case SPLIT_PENDINGINTENT_PENDINGINTENT -> mSystemUiProxy.startIntentsWithLegacyTransition(firstPI, firstUserId, firstShortcut, optionsBundle, secondPI, secondUserId, - secondShortcut, null /*options2*/, initialStagePosition, splitRatio, - adapter, shellInstanceId); + secondShortcut, null /*options2*/, initialStagePosition, + snapPosition, adapter, shellInstanceId); case SPLIT_SHORTCUT_TASK -> mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(firstShortcut, optionsBundle, secondTaskId, null /*options2*/, - initialStagePosition, splitRatio, adapter, shellInstanceId); + initialStagePosition, snapPosition, adapter, shellInstanceId); } } } /** * Used to launch split screen from a split pair that already exists (usually accessible through - * Overview). This is different than {@link #launchTasks(Consumer, boolean, float, InstanceId)} + * Overview). This is different than {@link #launchTasks(Consumer, boolean, int, InstanceId)} * in that this only launches split screen that are existing tasks. This doesn't determine which * API should be used (i.e. launching split with existing tasks vs intents vs shortcuts, etc). * @@ -447,7 +455,7 @@ public class SplitSelectStateController { */ public void launchExistingSplitPair(@Nullable GroupedTaskView groupedTaskView, int firstTaskId, int secondTaskId, @StagePosition int stagePosition, - Consumer callback, boolean freezeTaskList, float splitRatio) { + Consumer callback, boolean freezeTaskList, @SnapPosition int snapPosition) { mLaunchingTaskView = groupedTaskView; final ActivityOptions options1 = ActivityOptions.makeBasic(); if (freezeTaskList) { @@ -458,21 +466,20 @@ public class SplitSelectStateController { if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId, secondTaskId, callback, "LaunchExistingPair"); - mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, - null /* options2 */, stagePosition, splitRatio, - remoteTransition, null /*shellInstanceId*/); + mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, null /* options2 */, + stagePosition, snapPosition, remoteTransition, null /*shellInstanceId*/); } else { final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId, secondTaskId, callback); - mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle, - secondTaskId, null /* options2 */, stagePosition, - splitRatio, adapter, null /*shellInstanceId*/); + mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle, secondTaskId, + null /* options2 */, stagePosition, snapPosition, adapter, + null /*shellInstanceId*/); } } /** * Launches the initially selected task/intent in fullscreen (note the same SystemUi APIs are - * used as {@link #launchSplitTasks(Consumer)} because they are overloaded to launch both + * used as {@link #launchSplitTasks(int, Consumer)} because they are overloaded to launch both * split and fullscreen tasks) */ public void launchInitialAppFullscreen(Consumer callback) { @@ -497,14 +504,13 @@ public class SplitSelectStateController { switch (launchData.getSplitLaunchType()) { case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, null /* options2 */, initialStagePosition, - DEFAULT_SPLIT_RATIO, remoteTransition, instanceId); + SNAP_TO_50_50, remoteTransition, instanceId); case SPLIT_SINGLE_INTENT_FULLSCREEN -> mSystemUiProxy.startIntentAndTask(firstPI, firstUserId, optionsBundle, secondTaskId, null /*options2*/, - initialStagePosition, DEFAULT_SPLIT_RATIO, remoteTransition, - instanceId); + initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId); case SPLIT_SINGLE_SHORTCUT_FULLSCREEN -> mSystemUiProxy.startShortcutAndTask( initialShortcut, optionsBundle, firstTaskId, null /* options2 */, - initialStagePosition, DEFAULT_SPLIT_RATIO, remoteTransition, instanceId); + initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId); } } else { final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId, @@ -512,16 +518,15 @@ public class SplitSelectStateController { switch (launchData.getSplitLaunchType()) { case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasksWithLegacyTransition( firstTaskId, optionsBundle, secondTaskId, null /* options2 */, - initialStagePosition, DEFAULT_SPLIT_RATIO, adapter, instanceId); + initialStagePosition, SNAP_TO_50_50, adapter, instanceId); case SPLIT_SINGLE_INTENT_FULLSCREEN -> mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI, firstUserId, optionsBundle, secondTaskId, null /*options2*/, - initialStagePosition, DEFAULT_SPLIT_RATIO, adapter, - instanceId); + initialStagePosition, SNAP_TO_50_50, adapter, instanceId); case SPLIT_SINGLE_SHORTCUT_FULLSCREEN -> mSystemUiProxy.startShortcutAndTaskWithLegacyTransition( initialShortcut, optionsBundle, firstTaskId, null /* options2 */, - initialStagePosition, DEFAULT_SPLIT_RATIO, adapter, instanceId); + initialStagePosition, SNAP_TO_50_50, adapter, instanceId); } } } diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index b9ff272b04..510044d338 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -26,6 +26,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation; import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation; +import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell; import android.animation.TimeInterpolator; import android.content.Context; @@ -210,7 +211,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT; - mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition); + mPositionHelper.setSplitBounds(convertLauncherSplitBoundsToShell(mSplitBounds), + mStagePosition); } /** @@ -435,16 +437,4 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { // Ideally we should use square-root. This is an optimization as one of the dimension is 0. return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1])); } - - /** - * TODO(b/254378592): Remove this after consolidation of classes - */ - public static com.android.wm.shell.util.SplitBounds convertSplitBounds(SplitBounds bounds) { - return new com.android.wm.shell.util.SplitBounds( - bounds.leftTopBounds, - bounds.rightBottomBounds, - bounds.leftTopTaskId, - bounds.rightBottomTaskId - ); - } } diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index 01f6ae8d5c..7e58763b15 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -2,8 +2,8 @@ package com.android.quickstep.views; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; +import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell; import android.content.Context; import android.graphics.PointF; @@ -28,11 +28,11 @@ import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.util.CancellableTask; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.SplitSelectStateController; -import com.android.quickstep.util.TaskViewSimulator; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.recents.utilities.PreviewPositionHelper; import com.android.systemui.shared.system.InteractionJankMonitorWrapper; +import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; import java.util.HashMap; import java.util.function.Consumer; @@ -114,11 +114,11 @@ public class GroupedTaskView extends TaskView { if (mSplitBoundsConfig == null) { return; } - mSnapshotView.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator - .convertSplitBounds(splitBoundsConfig), + mSnapshotView.getPreviewPositionHelper().setSplitBounds( + convertLauncherSplitBoundsToShell(splitBoundsConfig), PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT); - mSnapshotView2.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator - .convertSplitBounds(splitBoundsConfig), + mSnapshotView2.getPreviewPositionHelper().setSplitBounds( + convertLauncherSplitBoundsToShell(splitBoundsConfig), PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT); } @@ -180,12 +180,20 @@ public class GroupedTaskView extends TaskView { invalidate(); } - public float getSplitRatio() { - if (mSplitBoundsConfig != null) { - return mSplitBoundsConfig.appsStackedVertically - ? mSplitBoundsConfig.topTaskPercent : mSplitBoundsConfig.leftTaskPercent; + @Nullable + public SplitBounds getSplitBoundsConfig() { + return mSplitBoundsConfig; + } + + /** + * Returns the {@link SnapPosition} of this pair of tasks. + */ + public int getSnapPosition() { + if (mSplitBoundsConfig == null) { + throw new IllegalStateException("mSplitBoundsConfig is null"); } - return DEFAULT_SPLIT_RATIO; + + return mSplitBoundsConfig.snapPosition; } @Override @@ -251,7 +259,7 @@ public class GroupedTaskView extends TaskView { getRecentsView().getSplitSelectController().launchExistingSplitPair( launchingExistingTaskView ? this : null, mTask.key.id, mSecondaryTask.key.id, SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, - callback, isQuickswitch, getSplitRatio()); + callback, isQuickswitch, getSnapPosition()); } @Override diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt index 4e7fcf07d9..f19874169b 100644 --- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt @@ -20,7 +20,6 @@ package com.android.quickstep.util import android.app.ActivityManager import android.app.PendingIntent import android.content.ComponentName -import android.content.Context import android.content.Intent import android.graphics.Rect import android.os.Handler @@ -38,7 +37,7 @@ import com.android.launcher3.util.withArgCaptor import com.android.quickstep.RecentsModel import com.android.quickstep.SystemUiProxy import com.android.systemui.shared.recents.model.Task -import java.util.function.Consumer +import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50 import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull @@ -50,6 +49,7 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import java.util.function.Consumer @RunWith(AndroidJUnit4::class) class SplitSelectStateControllerTest { @@ -560,7 +560,7 @@ class SplitSelectStateControllerTest { return GroupTask( task1, task2, - SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1) + SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_50_50) ) } @@ -592,7 +592,7 @@ class SplitSelectStateControllerTest { return GroupTask( task1, task2, - SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1) + SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_50_50) ) } } diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java index 1ae43d000b..f4a022544c 100644 --- a/src/com/android/launcher3/util/SplitConfigurationOptions.java +++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java @@ -77,11 +77,6 @@ public final class SplitConfigurationOptions { public @interface StageType {} /////////////////////////////////// - /** - * Default split ratio for launching app pair from overview. - */ - public static final float DEFAULT_SPLIT_RATIO = 0.5f; - public static class SplitPositionOption { public final int iconResId; public final int textResId; @@ -116,6 +111,8 @@ public final class SplitConfigurationOptions { public final float leftTaskPercent; public final float dividerWidthPercent; public final float dividerHeightPercent; + public final int snapPosition; + /** * If {@code true}, that means at the time of creation of this object, the * split-screened apps were vertically stacked. This is useful in scenarios like @@ -135,11 +132,12 @@ public final class SplitConfigurationOptions { public final int rightBottomTaskId; public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, - int rightBottomTaskId) { + int rightBottomTaskId, int snapPosition) { this.leftTopBounds = leftTopBounds; this.rightBottomBounds = rightBottomBounds; this.leftTopTaskId = leftTopTaskId; this.rightBottomTaskId = rightBottomTaskId; + this.snapPosition = snapPosition; if (rightBottomBounds.top > leftTopBounds.top) { // vertical apps, horizontal divider