summaryrefslogtreecommitdiff
path: root/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/src/com/android/quickstep/util/SplitSelectStateController.java')
-rw-r--r--quickstep/src/com/android/quickstep/util/SplitSelectStateController.java296
1 files changed, 210 insertions, 86 deletions
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d4ddf76c41..d5899e47f4 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -17,14 +17,10 @@
package com.android.quickstep.util;
import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM;
-import static com.android.launcher3.testing.shared.TestProtocol.LAUNCH_SPLIT_PAIR;
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
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;
@@ -35,15 +31,18 @@ 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.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
+import android.annotation.UiThread;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
@@ -72,14 +71,17 @@ import com.android.internal.logging.InstanceId;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.BackPressHandler;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.OverviewComponentObserver;
@@ -91,14 +93,14 @@ import com.android.quickstep.RecentsModel;
import com.android.quickstep.SplitSelectionListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.GroupedTaskView;
-import com.android.quickstep.views.SplitInstructionsView;
import com.android.quickstep.views.RecentsView;
+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.PersistentSnapPosition;
import com.android.wm.shell.splitscreen.ISplitSelectListener;
import java.io.PrintWriter;
@@ -114,9 +116,11 @@ import java.util.function.Consumer;
public class SplitSelectStateController {
private static final String TAG = "SplitSelectStateCtor";
- private Context mContext;
+ private StatefulActivity mContext;
private final Handler mHandler;
private final RecentsModel mRecentTasksModel;
+ @Nullable
+ private Runnable mActivityBackCallback;
private final SplitAnimationController mSplitAnimationController;
private final AppPairsController mAppPairsController;
private final SplitSelectDataHolder mSplitSelectDataHolder;
@@ -137,15 +141,38 @@ public class SplitSelectStateController {
/** If not null, this is the TaskView we want to launch from */
@Nullable
private GroupedTaskView mLaunchingTaskView;
+ /** If not null, this is the icon we want to launch from */
+ private AppPairIcon mLaunchingIconView;
+
+ /** True when the first selected split app is being launched in fullscreen. */
+ private boolean mLaunchingFirstAppFullscreen;
private FloatingTaskView mFirstFloatingTaskView;
private SplitInstructionsView mSplitInstructionsView;
private final List<SplitSelectionListener> mSplitSelectionListeners = new ArrayList<>();
- public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
- DepthController depthController, StatsLogManager statsLogManager,
- SystemUiProxy systemUiProxy, RecentsModel recentsModel) {
+ private final BackPressHandler mSplitBackHandler = new BackPressHandler() {
+ @Override
+ public boolean canHandleBack() {
+ return FeatureFlags.enableSplitContextually() && isSplitSelectActive();
+ }
+
+ @Override
+ public void onBackInvoked() {
+ // When exiting from split selection, leave current context to go to
+ // homescreen as well
+ getSplitAnimationController().playPlaceholderDismissAnim(mContext);
+ if (mActivityBackCallback != null) {
+ mActivityBackCallback.run();
+ }
+ }
+ };
+
+ public SplitSelectStateController(StatefulActivity context, Handler handler,
+ StateManager stateManager, DepthController depthController,
+ StatsLogManager statsLogManager, SystemUiProxy systemUiProxy, RecentsModel recentsModel,
+ Runnable activityBackCallback) {
mContext = context;
mHandler = handler;
mStatsLogManager = statsLogManager;
@@ -153,6 +180,7 @@ public class SplitSelectStateController {
mStateManager = stateManager;
mDepthController = depthController;
mRecentTasksModel = recentsModel;
+ mActivityBackCallback = activityBackCallback;
mSplitAnimationController = new SplitAnimationController(this);
mAppPairsController = new AppPairsController(context, this, statsLogManager);
mSplitSelectDataHolder = new SplitSelectDataHolder(mContext);
@@ -160,6 +188,9 @@ public class SplitSelectStateController {
public void onDestroy() {
mContext = null;
+ mActivityBackCallback = null;
+ mAppPairsController.onDestroy();
+ mSplitSelectDataHolder.onDestroy();
}
/**
@@ -185,15 +216,16 @@ public class SplitSelectStateController {
}
/**
- * Maps a List<ComponentKey> to List<@Nullable Task>, searching through active Tasks in
- * RecentsModel. If found, the Task will be the most recently-interacted-with instance of that
- * Task. Then runs the given callback on that List.
- * <p>
- * Used in various task-switching or splitscreen operations when we need to check if there is a
- * currently running Task of a certain type and use the most recent one.
+ * Given a list of task keys, searches through active Tasks in RecentsModel to find the last
+ * active instances of these tasks. Returns an empty array if there is no such running task.
+ *
+ * @param componentKeys The list of ComponentKeys to search for.
+ * @param callback The callback that will be executed on the list of found tasks.
+ * @param findExactPairMatch If {@code true}, only finds tasks that contain BOTH of the wanted
+ * tasks (i.e. searching for a running pair of tasks.)
*/
- public void findLastActiveTasksAndRunCallback(
- @Nullable List<ComponentKey> componentKeys, Consumer<List<Task>> callback) {
+ public void findLastActiveTasksAndRunCallback(@Nullable List<ComponentKey> componentKeys,
+ boolean findExactPairMatch, Consumer<List<Task>> callback) {
mRecentTasksModel.getTasks(taskGroups -> {
if (componentKeys == null || componentKeys.isEmpty()) {
callback.accept(Collections.emptyList());
@@ -201,27 +233,40 @@ public class SplitSelectStateController {
}
List<Task> lastActiveTasks = new ArrayList<>();
- // For each key we are looking for, add to lastActiveTasks with the corresponding Task
- // (or null if not found).
- for (ComponentKey key : componentKeys) {
- Task lastActiveTask = null;
+
+ if (findExactPairMatch) {
// Loop through tasks in reverse, since they are ordered with most-recent tasks last
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- Task task1 = groupTask.task1;
- // Don't add duplicate Tasks
- if (isInstanceOfComponent(task1, key) && !lastActiveTasks.contains(task1)) {
- lastActiveTask = task1;
- break;
- }
- Task task2 = groupTask.task2;
- if (isInstanceOfComponent(task2, key) && !lastActiveTasks.contains(task2)) {
- lastActiveTask = task2;
+ if (isInstanceOfAppPair(
+ groupTask, componentKeys.get(0), componentKeys.get(1))) {
+ lastActiveTasks.add(groupTask.task1);
break;
}
}
+ } else {
+ // For each key we are looking for, add to lastActiveTasks with the corresponding
+ // Task (or do nothing if not found).
+ for (ComponentKey key : componentKeys) {
+ Task lastActiveTask = null;
+ // Loop through tasks in reverse, since they are ordered with recent tasks last
+ for (int i = taskGroups.size() - 1; i >= 0; i--) {
+ GroupTask groupTask = taskGroups.get(i);
+ Task task1 = groupTask.task1;
+ // Don't add duplicate Tasks
+ if (isInstanceOfComponent(task1, key) && !lastActiveTasks.contains(task1)) {
+ lastActiveTask = task1;
+ break;
+ }
+ Task task2 = groupTask.task2;
+ if (isInstanceOfComponent(task2, key) && !lastActiveTasks.contains(task2)) {
+ lastActiveTask = task2;
+ break;
+ }
+ }
- lastActiveTasks.add(lastActiveTask);
+ lastActiveTasks.add(lastActiveTask);
+ }
}
callback.accept(lastActiveTasks);
@@ -243,6 +288,19 @@ public class SplitSelectStateController {
}
/**
+ * Checks if a given GroupTask is a pair of apps that matches two given ComponentKeys. We check
+ * both permutations because task order is not guaranteed in GroupTasks.
+ */
+ public boolean isInstanceOfAppPair(GroupTask groupTask, @NonNull ComponentKey componentKey1,
+ @NonNull ComponentKey componentKey2) {
+ return ((isInstanceOfComponent(groupTask.task1, componentKey1)
+ && isInstanceOfComponent(groupTask.task2, componentKey2))
+ ||
+ (isInstanceOfComponent(groupTask.task1, componentKey2)
+ && isInstanceOfComponent(groupTask.task2, componentKey1)));
+ }
+
+ /**
* Listener will only get callbacks going forward from the point of registration. No
* methods will be fired upon registering.
*/
@@ -267,11 +325,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<Boolean> callback) {
+ public void launchSplitTasks(@PersistentSnapPosition int snapPosition,
+ @Nullable Consumer<Boolean> callback) {
Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
LogUtils.getShellShareableInstanceId();
- launchTasks(callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
- instanceIds.first);
+ launchTasks(callback, false /* freezeTaskList */, snapPosition, instanceIds.first);
mStatsLogManager.logger()
.withItemInfo(mSplitSelectDataHolder.getItemInfo())
@@ -280,11 +338,26 @@ public class SplitSelectStateController {
}
/**
- * A version of {@link #launchTasks(Consumer, boolean, float, InstanceId)} with no success
+ * A version of {@link #launchTasks(Consumer, boolean, int, InstanceId)} with no success
* callback.
*/
+ public void launchSplitTasks(@PersistentSnapPosition int snapPosition) {
+ launchSplitTasks(snapPosition, /* callback */ null);
+ }
+
+ /**
+ * A version of {@link #launchSplitTasks(int, Consumer)} that launches with default split ratio.
+ */
+ public void launchSplitTasks(@Nullable Consumer<Boolean> 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);
}
/**
@@ -312,6 +385,10 @@ public class SplitSelectStateController {
mSplitSelectDataHolder.setSecondTask(pendingIntent);
}
+ public void setSecondWidget(PendingIntent pendingIntent, Intent widgetIntent) {
+ mSplitSelectDataHolder.setSecondWidget(pendingIntent, widgetIntent);
+ }
+
/**
* To be called when we want to launch split pairs from Overview. Split can be initiated from
* either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
@@ -321,7 +398,7 @@ public class SplitSelectStateController {
* foreground (quickswitch, launching previous pairs from overview)
*/
public void launchTasks(@Nullable Consumer<Boolean> callback, boolean freezeTaskList,
- float splitRatio, @Nullable InstanceId shellInstanceId) {
+ @PersistentSnapPosition int snapPosition, @Nullable InstanceId shellInstanceId) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "launchSplitTasks");
final ActivityOptions options1 = ActivityOptions.makeBasic();
@@ -337,44 +414,46 @@ public class SplitSelectStateController {
ShortcutInfo secondShortcut = launchData.getSecondShortcut();
PendingIntent firstPI = launchData.getInitialPendingIntent();
PendingIntent secondPI = launchData.getSecondPendingIntent();
+ Intent widgetIntent = launchData.getWidgetSecondIntent();
int firstUserId = launchData.getInitialUserId();
int secondUserId = launchData.getSecondUserId();
int initialStagePosition = launchData.getInitialStagePosition();
Bundle optionsBundle = options1.toBundle();
-
+ Bundle extrasBundle = new Bundle(1);
+ extrasBundle.putParcelable(KEY_EXTRA_WIDGET_INTENT, widgetIntent);
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId,
secondTaskId, callback, "LaunchSplitPair");
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, extrasBundle, 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,
- remoteTransition, shellInstanceId);
+ optionsBundle, secondPI, secondUserId, secondShortcut, extrasBundle,
+ 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 {
@@ -384,40 +463,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).
*
@@ -426,7 +505,8 @@ public class SplitSelectStateController {
*/
public void launchExistingSplitPair(@Nullable GroupedTaskView groupedTaskView,
int firstTaskId, int secondTaskId, @StagePosition int stagePosition,
- Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+ Consumer<Boolean> callback, boolean freezeTaskList,
+ @PersistentSnapPosition int snapPosition) {
mLaunchingTaskView = groupedTaskView;
final ActivityOptions options1 = ActivityOptions.makeBasic();
if (freezeTaskList) {
@@ -437,21 +517,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<Boolean> callback) {
@@ -476,14 +555,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,
@@ -491,16 +569,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);
}
}
}
@@ -564,20 +641,19 @@ public class SplitSelectStateController {
private final int mInitialTaskId;
private final int mSecondTaskId;
- private final Consumer<Boolean> mSuccessCallback;
+ private Consumer<Boolean> mFinishCallback;
RemoteSplitLaunchTransitionRunner(int initialTaskId, int secondTaskId,
@Nullable Consumer<Boolean> callback) {
mInitialTaskId = initialTaskId;
mSecondTaskId = secondTaskId;
- mSuccessCallback = callback;
+ mFinishCallback = callback;
}
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
- testLogD(LAUNCH_SPLIT_PAIR, "Received split startAnimation");
final Runnable finishAdapter = () -> {
try {
finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
@@ -587,13 +663,22 @@ public class SplitSelectStateController {
};
MAIN_EXECUTOR.execute(() -> {
- TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
- mDepthController, mInitialTaskId, mSecondTaskId, info, t, () -> {
+ // Only animate from taskView if it's already visible
+ boolean shouldLaunchFromTaskView = mLaunchingTaskView != null &&
+ mLaunchingTaskView.getRecentsView().isTaskViewVisible(mLaunchingTaskView);
+ mSplitAnimationController.playSplitLaunchAnimation(
+ shouldLaunchFromTaskView ? mLaunchingTaskView : null,
+ mLaunchingIconView,
+ mInitialTaskId,
+ mSecondTaskId,
+ null /* apps */,
+ null /* wallpapers */,
+ null /* nonApps */,
+ mStateManager,
+ mDepthController,
+ info, t, () -> {
finishAdapter.run();
- if (mSuccessCallback != null) {
- mSuccessCallback.accept(true);
- }
- resetState();
+ cleanup(true /*success*/);
});
});
}
@@ -602,6 +687,27 @@ public class SplitSelectStateController {
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishedCallback) { }
+
+ @Override
+ public void onTransitionConsumed(IBinder transition, boolean aborted)
+ throws RemoteException {
+ MAIN_EXECUTOR.execute(() -> {
+ cleanup(false /*success*/);
+ });
+ }
+
+ /**
+ * Must be called on UI thread.
+ * @param success if launching the split apps occurred successfully or not
+ */
+ @UiThread
+ private void cleanup(boolean success) {
+ if (mFinishCallback != null) {
+ mFinishCallback.accept(success);
+ mFinishCallback = null;
+ }
+ resetState();
+ }
}
/**
@@ -626,9 +732,10 @@ public class SplitSelectStateController {
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
Runnable finishedCallback) {
postAsyncCallback(mHandler,
- () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
- mLaunchingTaskView, mInitialTaskId, mSecondTaskId, apps, wallpapers,
- nonApps, mStateManager, mDepthController, () -> {
+ () -> mSplitAnimationController.playSplitLaunchAnimation(mLaunchingTaskView,
+ mLaunchingIconView, mInitialTaskId, mSecondTaskId, apps, wallpapers,
+ nonApps, mStateManager, mDepthController, null /* info */, null /* t */,
+ () -> {
finishedCallback.run();
if (mSuccessCallback != null) {
mSuccessCallback.accept(true);
@@ -652,7 +759,7 @@ public class SplitSelectStateController {
/**
* To be called whenever we exit split selection state. If
- * {@link FeatureFlags#ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE} is set, this should be the
+ * {@link FeatureFlags#enableSplitContextually()} is set, this should be the
* central way split is getting reset, which should then go through the callbacks to reset
* other state.
*/
@@ -661,10 +768,12 @@ public class SplitSelectStateController {
dispatchOnSplitSelectionExit();
mRecentsAnimationRunning = false;
mLaunchingTaskView = null;
+ mLaunchingIconView = null;
mAnimateCurrentTaskDismissal = false;
mDismissingFromSplitPair = false;
mFirstFloatingTaskView = null;
mSplitInstructionsView = null;
+ mLaunchingFirstAppFullscreen = false;
}
/**
@@ -683,6 +792,10 @@ public class SplitSelectStateController {
return mSplitSelectDataHolder.isBothSplitAppsConfirmed();
}
+ public boolean isLaunchingFirstAppFullscreen() {
+ return mLaunchingFirstAppFullscreen;
+ }
+
public int getInitialTaskId() {
return mSplitSelectDataHolder.getInitialTaskId();
}
@@ -691,6 +804,9 @@ public class SplitSelectStateController {
return mSplitSelectDataHolder.getSecondTaskId();
}
+ public void setLaunchingFirstAppFullscreen() {
+ mLaunchingFirstAppFullscreen = true;
+ }
public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) {
mFirstFloatingTaskView = floatingTaskView;
}
@@ -713,6 +829,14 @@ public class SplitSelectStateController {
return mAppPairsController;
}
+ public void setLaunchingIconView(AppPairIcon launchingIconView) {
+ mLaunchingIconView = launchingIconView;
+ }
+
+ public BackPressHandler getSplitBackHandler() {
+ return mSplitBackHandler;
+ }
+
public void dump(String prefix, PrintWriter writer) {
if (mSplitSelectDataHolder != null) {
mSplitSelectDataHolder.dump(prefix, writer);
@@ -744,7 +868,7 @@ public class SplitSelectStateController {
@Override
public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
- if (!ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE.get()) return false;
+ if (!isDesktopModeSupported()) return false;
MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition,
taskBounds));
return true;