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.java259
1 files changed, 132 insertions, 127 deletions
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index a147b6808c..5253e8c657 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -16,158 +16,159 @@
package com.android.quickstep.util;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
+import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.util.Executors.MAIN_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.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
-import android.animation.AnimatorSet;
import android.app.ActivityOptions;
-import android.content.res.Resources;
+import android.app.ActivityThread;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.util.Pair;
-import android.view.Gravity;
+import android.view.RemoteAnimationAdapter;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.LauncherAnimationRunner;
-import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
-import com.android.launcher3.R;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.RemoteTransitionRunner;
+import java.util.function.Consumer;
+
/**
* Represent data needed for the transient state when user has selected one app for split screen
* and is in the process of either a) selecting a second app or b) exiting intention to invoke split
*/
public class SplitSelectStateController {
- private final SystemUiProxy mSystemUiProxy;
- private TaskView mInitialTaskView;
- private SplitPositionOption mInitialPosition;
- private Rect mInitialBounds;
private final Handler mHandler;
+ private final SystemUiProxy mSystemUiProxy;
+ private final StateManager mStateManager;
+ private final DepthController mDepthController;
+ private @StagePosition int mStagePosition;
+ private Task mInitialTask;
+ private Task mSecondTask;
+ private boolean mRecentsAnimationRunning;
+ /** If not null, this is the TaskView we want to launch from */
+ @Nullable
+ private GroupedTaskView mLaunchingTaskView;
- public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
- mSystemUiProxy = systemUiProxy;
+ public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy,
+ StateManager stateManager,
+ DepthController depthController) {
mHandler = handler;
+ mSystemUiProxy = systemUiProxy;
+ mStateManager = stateManager;
+ mDepthController = depthController;
}
/**
* To be called after first task selected
*/
- public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+ public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
Rect initialBounds) {
- mInitialTaskView = taskView;
- mInitialPosition = positionOption;
- mInitialBounds = initialBounds;
+ mInitialTask = task;
+ mStagePosition = stagePosition;
}
/**
* To be called after second task selected
*/
- public void setSecondTaskId(TaskView taskView) {
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- // Assume initial task is for top/left part of screen
- final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
- : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
+ public void setSecondTaskId(Task task, Consumer<Boolean> callback) {
+ mSecondTask = task;
+ launchTasks(mInitialTask, mSecondTask, mStagePosition, callback,
+ false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
+ }
- RemoteSplitLaunchAnimationRunner animationRunner =
- new RemoteSplitLaunchAnimationRunner(mInitialTaskView, taskView);
- mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
- null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
- new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR));
- return;
- }
- // Assume initial mInitialTaskId is for top/left part of screen
- RemoteAnimationFactory initialSplitRunnerWrapped = new SplitLaunchAnimationRunner(
- mInitialTaskView, 0);
- RemoteAnimationFactory secondarySplitRunnerWrapped = new SplitLaunchAnimationRunner(
- taskView, 1);
- RemoteAnimationRunnerCompat initialSplitRunner = new LauncherAnimationRunner(
- new Handler(Looper.getMainLooper()), initialSplitRunnerWrapped,
- true /* startAtFrontOfQueue */);
- RemoteAnimationRunnerCompat secondarySplitRunner = new LauncherAnimationRunner(
- new Handler(Looper.getMainLooper()), secondarySplitRunnerWrapped,
- true /* startAtFrontOfQueue */);
- ActivityOptions initialOptions = ActivityOptionsCompat.makeRemoteAnimation(
- new RemoteAnimationAdapterCompat(initialSplitRunner, 300, 150));
- ActivityOptions secondaryOptions = ActivityOptionsCompat.makeRemoteAnimation(
- new RemoteAnimationAdapterCompat(secondarySplitRunner, 300, 150));
- mSystemUiProxy.startTask(mInitialTaskView.getTask().key.id, mInitialPosition.mStageType,
- mInitialPosition.mStagePosition,
- /*null*/ initialOptions.toBundle());
- Pair<Integer, Integer> compliment = getComplimentaryStageAndPosition(mInitialPosition);
- mSystemUiProxy.startTask(taskView.getTask().key.id, compliment.first,
- compliment.second,
- /*null*/ secondaryOptions.toBundle());
- // After successful launch, call resetState
- resetState();
+ /**
+ * To be called when we want to launch split pairs from an existing GroupedTaskView.
+ */
+ public void launchTasks(GroupedTaskView groupedTaskView,
+ Consumer<Boolean> callback, boolean freezeTaskList) {
+ mLaunchingTaskView = groupedTaskView;
+ TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
+ groupedTaskView.getTaskIdAttributeContainers();
+ launchTasks(taskIdAttributeContainers[0].getTask(), taskIdAttributeContainers[1].getTask(),
+ taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList,
+ groupedTaskView.getSplitRatio());
}
/**
- * @return {@link InsettableFrameLayout.LayoutParams} to correctly position the
- * split placeholder view
+ * @param stagePosition representing location of task1
*/
- public InsettableFrameLayout.LayoutParams getLayoutParamsForActivePosition(Resources resources,
- DeviceProfile deviceProfile) {
- InsettableFrameLayout.LayoutParams params =
- new InsettableFrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
- boolean topLeftPosition = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT;
- if (deviceProfile.isLandscape) {
- params.width = (int) resources.getDimension(R.dimen.split_placeholder_size);
- params.gravity = topLeftPosition ? Gravity.START : Gravity.END;
+ public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,
+ Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+ // Assume initial task is for top/left part of screen
+ final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? new int[]{task1.key.id, task2.key.id}
+ : new int[]{task2.key.id, task1.key.id};
+ if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+ RemoteSplitLaunchTransitionRunner animationRunner =
+ new RemoteSplitLaunchTransitionRunner(task1, task2);
+ mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
+ null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
+ new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
+ ActivityThread.currentActivityThread().getApplicationThread()));
} else {
- params.height = (int) resources.getDimension(R.dimen.split_placeholder_size);
- params.gravity = Gravity.TOP;
+ RemoteSplitLaunchAnimationRunner animationRunner =
+ new RemoteSplitLaunchAnimationRunner(task1, task2, callback);
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
+ 300, 150,
+ ActivityThread.currentActivityThread().getApplicationThread());
+
+ ActivityOptions mainOpts = ActivityOptions.makeBasic();
+ if (freezeTaskList) {
+ mainOpts.setFreezeRecentTasksReordering();
+ }
+ mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
+ taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+ splitRatio, adapter);
}
+ }
- return params;
+ public @StagePosition int getActiveSplitStagePosition() {
+ return mStagePosition;
}
- @Nullable
- public SplitPositionOption getActiveSplitPositionOption() {
- return mInitialPosition;
+ public void setRecentsAnimationRunning(boolean running) {
+ this.mRecentsAnimationRunning = running;
}
/**
* Requires Shell Transitions
*/
- private class RemoteSplitLaunchAnimationRunner implements RemoteTransitionRunner {
+ private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner {
- private final TaskView mInitialTaskView;
- private final TaskView mTaskView;
+ private final Task mInitialTask;
+ private final Task mSecondTask;
- RemoteSplitLaunchAnimationRunner(TaskView initialTaskView, TaskView taskView) {
- mInitialTaskView = initialTaskView;
- mTaskView = taskView;
+ RemoteSplitLaunchTransitionRunner(Task initialTask, Task secondTask) {
+ mInitialTask = initialTask;
+ mSecondTask = secondTask;
}
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, Runnable finishCallback) {
- TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskView, mTaskView,
- info, t, finishCallback);
+ TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTask,
+ mSecondTask, info, t, finishCallback);
// After successful launch, call resetState
resetState();
}
@@ -175,62 +176,66 @@ public class SplitSelectStateController {
/**
* LEGACY
- * @return the opposite stage and position from the {@param position} provided as first and
- * second object, respectively
- * Ex. If position is has stage = Main and position = Top/Left, this will return
- * Pair(stage=Side, position=Bottom/Left)
- */
- private Pair<Integer, Integer> getComplimentaryStageAndPosition(SplitPositionOption position) {
- // Right now this is as simple as flipping between 0 and 1
- int complimentStageType = position.mStageType ^ 1;
- int complimentStagePosition = position.mStagePosition ^ 1;
- return new Pair<>(complimentStageType, complimentStagePosition);
- }
-
- /**
- * LEGACY
* Remote animation runner for animation to launch an app.
*/
- private class SplitLaunchAnimationRunner implements RemoteAnimationFactory {
+ private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat {
- private final TaskView mV;
- private final int mTargetState;
+ private final Task mInitialTask;
+ private final Task mSecondTask;
+ private final Consumer<Boolean> mSuccessCallback;
- SplitLaunchAnimationRunner(TaskView v, int targetState) {
- mV = v;
- mTargetState = targetState;
+ RemoteSplitLaunchAnimationRunner(Task initialTask, Task secondTask,
+ Consumer<Boolean> successCallback) {
+ mInitialTask = initialTask;
+ mSecondTask = secondTask;
+ mSuccessCallback = successCallback;
}
@Override
- public void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
- LauncherAnimationRunner.AnimationResult result) {
- AnimatorSet anim = new AnimatorSet();
- BaseQuickstepLauncher activity = BaseActivity.fromContext(mV.getContext());
- TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(anim, mV,
- appTargets, wallpaperTargets, nonAppTargets, true, activity.getStateManager(),
- activity.getDepthController(), mTargetState);
- result.setAnimation(anim, activity);
+ public void onAnimationStart(int transit, RemoteAnimationTargetCompat[] apps,
+ RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ Runnable finishedCallback) {
+ postAsyncCallback(mHandler,
+ () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
+ mLaunchingTaskView, mInitialTask, mSecondTask, apps, wallpapers,
+ nonApps, mStateManager, mDepthController, () -> {
+ finishedCallback.run();
+ if (mSuccessCallback != null) {
+ mSuccessCallback.accept(true);
+ }
+ resetState();
+ }));
}
- }
+ @Override
+ public void onAnimationCancelled() {
+ postAsyncCallback(mHandler, () -> {
+ if (mSuccessCallback != null) {
+ // Launching legacy tasks while recents animation is running will always cause
+ // onAnimationCancelled to be called (should be fixed w/ shell transitions?)
+ mSuccessCallback.accept(mRecentsAnimationRunning);
+ }
+ resetState();
+ });
+ }
+ }
/**
* To be called if split select was cancelled
*/
public void resetState() {
- mInitialTaskView = null;
- mInitialPosition = null;
- mInitialBounds = null;
+ mInitialTask = null;
+ mSecondTask = null;
+ mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+ mRecentsAnimationRunning = false;
+ mLaunchingTaskView = null;
}
+ /**
+ * @return {@code true} if first task has been selected and waiting for the second task to be
+ * chosen
+ */
public boolean isSplitSelectActive() {
- return mInitialTaskView != null;
- }
-
- public Rect getInitialBounds() {
- return mInitialBounds;
+ return mInitialTask != null && mSecondTask == null;
}
}