diff options
Diffstat (limited to 'quickstep/src/com/android/quickstep/util/SplitSelectStateController.java')
-rw-r--r-- | quickstep/src/com/android/quickstep/util/SplitSelectStateController.java | 259 |
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; } } |