Loading quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java +44 −0 Original line number Diff line number Diff line Loading @@ -15,14 +15,23 @@ */ package com.android.launcher3.popup; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.View; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.quickstep.views.RecentsView; public interface QuickstepSystemShortcut { String TAG = QuickstepSystemShortcut.class.getSimpleName(); static SystemShortcut.Factory<BaseQuickstepLauncher> getSplitSelectShortcutByPosition( SplitPositionOption position) { return (activity, itemInfo) -> new QuickstepSystemShortcut.SplitSelectSystemShortcut( Loading @@ -46,6 +55,41 @@ public interface QuickstepSystemShortcut { @Override public void onClick(View view) { Bitmap bitmap; Intent intent; if (mItemInfo instanceof WorkspaceItemInfo) { final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo; bitmap = workspaceItemInfo.bitmap.icon; intent = workspaceItemInfo.intent; } else if (mItemInfo instanceof com.android.launcher3.model.data.AppInfo) { final com.android.launcher3.model.data.AppInfo appInfo = (com.android.launcher3.model.data.AppInfo) mItemInfo; bitmap = appInfo.bitmap.icon; intent = appInfo.intent; } else { Log.e(TAG, "unknown item type"); return; } RecentsView recentsView = mLauncher.getOverviewPanel(); recentsView.initiateSplitSelect( new SplitSelectSource(view, new BitmapDrawable(bitmap), intent, mPosition)); } } class SplitSelectSource { public final View view; public final Drawable drawable; public final Intent intent; public final SplitPositionOption position; public SplitSelectSource(View view, Drawable drawable, Intent intent, SplitPositionOption position) { this.view = view; this.drawable = drawable; this.intent = intent; this.position = position; } } } quickstep/src/com/android/quickstep/SystemUiProxy.java +15 −0 Original line number Diff line number Diff line Loading @@ -608,6 +608,21 @@ public class SystemUiProxy implements ISystemUiProxy, } } public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions, Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) { if (mSystemUiProxy != null) { try { mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent, taskId, intentFirst, mainOptions, sideOptions, sidePosition, splitRatio, adapter); } catch (RemoteException e) { Log.w(TAG, "Failed call startTasksWithLegacyTransition"); } } } public void startShortcut(String packageName, String shortcutId, int position, Bundle options, UserHandle user) { if (mSplitScreen != null) { Loading quickstep/src/com/android/quickstep/TaskViewUtils.java +30 −16 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.quickstep; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; Loading Loading @@ -46,6 +47,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.graphics.Matrix; Loading Loading @@ -389,18 +391,20 @@ public final class TaskViewUtils { * device is considered in multiWindowMode and things like insets and stuff change * and calculations have to be adjusted in the animations for that */ public static void composeRecentsSplitLaunchAnimator(@NonNull Task initalTask, @NonNull Task secondTask, @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { final TransitionInfo.Change[] splitRoots = new TransitionInfo.Change[2]; public static void composeRecentsSplitLaunchAnimator(int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId, @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { // TODO: consider initialTaskPendingIntent TransitionInfo.Change splitRoot1 = null; TransitionInfo.Change splitRoot2 = null; for (int i = 0; i < transitionInfo.getChanges().size(); ++i) { final TransitionInfo.Change change = transitionInfo.getChanges().get(i); final int taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1; final int mode = change.getMode(); // Find the target tasks' root tasks since those are the split stages that need to // be animated (the tasks themselves are children and thus inherit animation). if (taskId == initalTask.key.id || taskId == secondTask.key.id) { if (taskId == initialTaskId || taskId == secondTaskId) { if (!(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { throw new IllegalStateException( "Expected task to be showing, but it is " + mode); Loading @@ -409,16 +413,18 @@ public final class TaskViewUtils { throw new IllegalStateException("Initiating multi-split launch but the split" + "root of " + taskId + " is already visible or has broken hierarchy."); } splitRoots[taskId == initalTask.key.id ? 0 : 1] = transitionInfo.getChange(change.getParent()); } if (taskId == initialTaskId && initialTaskId != INVALID_TASK_ID) { splitRoot1 = transitionInfo.getChange(change.getParent()); } if (taskId == secondTaskId) { splitRoot2 = transitionInfo.getChange(change.getParent()); } } // This is where we should animate the split roots. For now, though, just make them visible. for (int i = 0; i < 2; ++i) { t.show(splitRoots[i].getLeash()); t.setAlpha(splitRoots[i].getLeash(), 1.f); } animateSplitRoot(t, splitRoot1); animateSplitRoot(t, splitRoot2); // This contains the initial state (before animation), so apply this at the beginning of // the animation. Loading @@ -428,6 +434,14 @@ public final class TaskViewUtils { finishCallback.run(); } private static void animateSplitRoot(SurfaceControl.Transaction t, TransitionInfo.Change splitRoot) { if (splitRoot != null) { t.show(splitRoot.getLeash()); t.setAlpha(splitRoot.getLeash(), 1.f); } } /** * Legacy version (until shell transitions are enabled) * Loading @@ -440,9 +454,9 @@ public final class TaskViewUtils { * If it is null, then it will simply fade in the starting apps and fade out launcher (for the * case where launcher handles animating starting split tasks from app icon) */ public static void composeRecentsSplitLaunchAnimatorLegacy( @Nullable GroupedTaskView launchingTaskView, @NonNull Task initialTask, @NonNull Task secondTask, @NonNull RemoteAnimationTargetCompat[] appTargets, @Nullable GroupedTaskView launchingTaskView, int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, @NonNull RemoteAnimationTargetCompat[] nonAppTargets, @NonNull StateManager stateManager, Loading Loading @@ -478,7 +492,7 @@ public final class TaskViewUtils { if (mode == MODE_OPENING) { openingTargets.add(leash); } else if (taskId == initialTask.key.id || taskId == secondTask.key.id) { } else if (taskId == initialTaskId || taskId == secondTaskId) { throw new IllegalStateException("Expected task to be opening, but it is " + mode); } else if (mode == MODE_CLOSING) { closingTargets.add(leash); Loading quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +7 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.popup.QuickstepSystemShortcut; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.FallbackActivityInterface; Loading Loading @@ -254,4 +255,10 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta // Do not let touch escape to siblings below this view. return result || mActivity.getStateManager().getState().overviewUi(); } @Override public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) { super.initiateSplitSelect(splitSelectSource); mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT); } } quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +69 −41 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.quickstep.util; import static android.app.ActivityTaskManager.INVALID_TASK_ID; 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; Loading @@ -24,7 +26,8 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO import android.app.ActivityOptions; import android.app.ActivityThread; import android.graphics.Rect; import android.app.PendingIntent; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.view.RemoteAnimationAdapter; Loading @@ -42,7 +45,6 @@ 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.recents.model.Task; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; Loading @@ -62,16 +64,16 @@ public class SplitSelectStateController { private final StateManager mStateManager; private final DepthController mDepthController; private @StagePosition int mStagePosition; private Task mInitialTask; private Task mSecondTask; private PendingIntent mInitialTaskPendingIntent; private int mInitialTaskId = INVALID_TASK_ID; private int mSecondTaskId = INVALID_TASK_ID; 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, StateManager stateManager, DepthController depthController) { StateManager stateManager, DepthController depthController) { mHandler = handler; mSystemUiProxy = systemUiProxy; mStateManager = stateManager; Loading @@ -81,19 +83,26 @@ public class SplitSelectStateController { /** * To be called after first task selected */ public void setInitialTaskSelect(Task task, @StagePosition int stagePosition, Rect initialBounds) { mInitialTask = task; public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) { mInitialTaskId = taskId; mStagePosition = stagePosition; mInitialTaskPendingIntent = null; } public void setInitialTaskSelect(PendingIntent pendingIntent, @StagePosition int stagePosition) { mInitialTaskPendingIntent = pendingIntent; mStagePosition = stagePosition; mInitialTaskId = INVALID_TASK_ID; } /** * To be called after second task selected */ public void setSecondTaskId(Task task, Consumer<Boolean> callback) { mSecondTask = task; launchTasks(mInitialTask, mSecondTask, mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO); public void setSecondTaskId(int taskId, Consumer<Boolean> callback) { mSecondTaskId = taskId; launchTasks(mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO); } /** Loading @@ -104,7 +113,8 @@ public class SplitSelectStateController { mLaunchingTaskView = groupedTaskView; TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers = groupedTaskView.getTaskIdAttributeContainers(); launchTasks(taskIdAttributeContainers[0].getTask(), taskIdAttributeContainers[1].getTask(), launchTasks(taskIdAttributeContainers[0].getTask().key.id, null, taskIdAttributeContainers[1].getTask().key.id, taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList, groupedTaskView.getSplitRatio()); } Loading @@ -112,22 +122,25 @@ public class SplitSelectStateController { /** * @param stagePosition representing location of task1 */ public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition, Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) { public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent, int taskId2, @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}; ? new int[]{taskId1, taskId2} : new int[]{taskId2, taskId1}; if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { RemoteSplitLaunchTransitionRunner animationRunner = new RemoteSplitLaunchTransitionRunner(task1, task2); new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2); mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR, ActivityThread.currentActivityThread().getApplicationThread())); // TODO: handle intent + task with shell transition } else { RemoteSplitLaunchAnimationRunner animationRunner = new RemoteSplitLaunchAnimationRunner(task1, task2, callback); new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2, callback); final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner), 300, 150, Loading @@ -137,9 +150,16 @@ public class SplitSelectStateController { if (freezeTaskList) { mainOpts.setFreezeRecentTasksReordering(); } if (taskPendingIntent == null) { mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(), taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, adapter); } else { mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent, new Intent(), taskId2, stagePosition == STAGE_POSITION_TOP_OR_LEFT, mainOpts.toBundle(), null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, adapter); } } } Loading @@ -156,19 +176,22 @@ public class SplitSelectStateController { */ private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner { private final Task mInitialTask; private final Task mSecondTask; private final int mInitialTaskId; private final PendingIntent mInitialTaskPendingIntent; private final int mSecondTaskId; RemoteSplitLaunchTransitionRunner(Task initialTask, Task secondTask) { mInitialTask = initialTask; mSecondTask = secondTask; RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent, int secondTaskId) { mInitialTaskId = initialTaskId; mInitialTaskPendingIntent = initialTaskPendingIntent; mSecondTaskId = secondTaskId; } @Override public void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, Runnable finishCallback) { TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTask, mSecondTask, info, t, finishCallback); TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, info, t, finishCallback); // After successful launch, call resetState resetState(); } Loading @@ -180,14 +203,16 @@ public class SplitSelectStateController { */ private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat { private final Task mInitialTask; private final Task mSecondTask; private final int mInitialTaskId; private final PendingIntent mInitialTaskPendingIntent; private final int mSecondTaskId; private final Consumer<Boolean> mSuccessCallback; RemoteSplitLaunchAnimationRunner(Task initialTask, Task secondTask, Consumer<Boolean> successCallback) { mInitialTask = initialTask; mSecondTask = secondTask; RemoteSplitLaunchAnimationRunner(int initialTaskId, PendingIntent initialTaskPendingIntent, int secondTaskId, Consumer<Boolean> successCallback) { mInitialTaskId = initialTaskId; mInitialTaskPendingIntent = initialTaskPendingIntent; mSecondTaskId = secondTaskId; mSuccessCallback = successCallback; } Loading @@ -197,8 +222,9 @@ public class SplitSelectStateController { Runnable finishedCallback) { postAsyncCallback(mHandler, () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy( mLaunchingTaskView, mInitialTask, mSecondTask, apps, wallpapers, nonApps, mStateManager, mDepthController, () -> { mLaunchingTaskView, mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, apps, wallpapers, nonApps, mStateManager, mDepthController, () -> { finishedCallback.run(); if (mSuccessCallback != null) { mSuccessCallback.accept(true); Loading @@ -224,8 +250,9 @@ public class SplitSelectStateController { * To be called if split select was cancelled */ public void resetState() { mInitialTask = null; mSecondTask = null; mInitialTaskId = INVALID_TASK_ID; mInitialTaskPendingIntent = null; mSecondTaskId = INVALID_TASK_ID; mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; mRecentsAnimationRunning = false; mLaunchingTaskView = null; Loading @@ -236,6 +263,7 @@ public class SplitSelectStateController { * chosen */ public boolean isSplitSelectActive() { return mInitialTask != null && mSecondTask == null; return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskPendingIntent != null) && mSecondTaskId == INVALID_TASK_ID; } } Loading
quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java +44 −0 Original line number Diff line number Diff line Loading @@ -15,14 +15,23 @@ */ package com.android.launcher3.popup; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.View; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.quickstep.views.RecentsView; public interface QuickstepSystemShortcut { String TAG = QuickstepSystemShortcut.class.getSimpleName(); static SystemShortcut.Factory<BaseQuickstepLauncher> getSplitSelectShortcutByPosition( SplitPositionOption position) { return (activity, itemInfo) -> new QuickstepSystemShortcut.SplitSelectSystemShortcut( Loading @@ -46,6 +55,41 @@ public interface QuickstepSystemShortcut { @Override public void onClick(View view) { Bitmap bitmap; Intent intent; if (mItemInfo instanceof WorkspaceItemInfo) { final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo; bitmap = workspaceItemInfo.bitmap.icon; intent = workspaceItemInfo.intent; } else if (mItemInfo instanceof com.android.launcher3.model.data.AppInfo) { final com.android.launcher3.model.data.AppInfo appInfo = (com.android.launcher3.model.data.AppInfo) mItemInfo; bitmap = appInfo.bitmap.icon; intent = appInfo.intent; } else { Log.e(TAG, "unknown item type"); return; } RecentsView recentsView = mLauncher.getOverviewPanel(); recentsView.initiateSplitSelect( new SplitSelectSource(view, new BitmapDrawable(bitmap), intent, mPosition)); } } class SplitSelectSource { public final View view; public final Drawable drawable; public final Intent intent; public final SplitPositionOption position; public SplitSelectSource(View view, Drawable drawable, Intent intent, SplitPositionOption position) { this.view = view; this.drawable = drawable; this.intent = intent; this.position = position; } } }
quickstep/src/com/android/quickstep/SystemUiProxy.java +15 −0 Original line number Diff line number Diff line Loading @@ -608,6 +608,21 @@ public class SystemUiProxy implements ISystemUiProxy, } } public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions, Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) { if (mSystemUiProxy != null) { try { mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent, taskId, intentFirst, mainOptions, sideOptions, sidePosition, splitRatio, adapter); } catch (RemoteException e) { Log.w(TAG, "Failed call startTasksWithLegacyTransition"); } } } public void startShortcut(String packageName, String shortcutId, int position, Bundle options, UserHandle user) { if (mSplitScreen != null) { Loading
quickstep/src/com/android/quickstep/TaskViewUtils.java +30 −16 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.quickstep; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; Loading Loading @@ -46,6 +47,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.graphics.Matrix; Loading Loading @@ -389,18 +391,20 @@ public final class TaskViewUtils { * device is considered in multiWindowMode and things like insets and stuff change * and calculations have to be adjusted in the animations for that */ public static void composeRecentsSplitLaunchAnimator(@NonNull Task initalTask, @NonNull Task secondTask, @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { final TransitionInfo.Change[] splitRoots = new TransitionInfo.Change[2]; public static void composeRecentsSplitLaunchAnimator(int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId, @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { // TODO: consider initialTaskPendingIntent TransitionInfo.Change splitRoot1 = null; TransitionInfo.Change splitRoot2 = null; for (int i = 0; i < transitionInfo.getChanges().size(); ++i) { final TransitionInfo.Change change = transitionInfo.getChanges().get(i); final int taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1; final int mode = change.getMode(); // Find the target tasks' root tasks since those are the split stages that need to // be animated (the tasks themselves are children and thus inherit animation). if (taskId == initalTask.key.id || taskId == secondTask.key.id) { if (taskId == initialTaskId || taskId == secondTaskId) { if (!(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { throw new IllegalStateException( "Expected task to be showing, but it is " + mode); Loading @@ -409,16 +413,18 @@ public final class TaskViewUtils { throw new IllegalStateException("Initiating multi-split launch but the split" + "root of " + taskId + " is already visible or has broken hierarchy."); } splitRoots[taskId == initalTask.key.id ? 0 : 1] = transitionInfo.getChange(change.getParent()); } if (taskId == initialTaskId && initialTaskId != INVALID_TASK_ID) { splitRoot1 = transitionInfo.getChange(change.getParent()); } if (taskId == secondTaskId) { splitRoot2 = transitionInfo.getChange(change.getParent()); } } // This is where we should animate the split roots. For now, though, just make them visible. for (int i = 0; i < 2; ++i) { t.show(splitRoots[i].getLeash()); t.setAlpha(splitRoots[i].getLeash(), 1.f); } animateSplitRoot(t, splitRoot1); animateSplitRoot(t, splitRoot2); // This contains the initial state (before animation), so apply this at the beginning of // the animation. Loading @@ -428,6 +434,14 @@ public final class TaskViewUtils { finishCallback.run(); } private static void animateSplitRoot(SurfaceControl.Transaction t, TransitionInfo.Change splitRoot) { if (splitRoot != null) { t.show(splitRoot.getLeash()); t.setAlpha(splitRoot.getLeash(), 1.f); } } /** * Legacy version (until shell transitions are enabled) * Loading @@ -440,9 +454,9 @@ public final class TaskViewUtils { * If it is null, then it will simply fade in the starting apps and fade out launcher (for the * case where launcher handles animating starting split tasks from app icon) */ public static void composeRecentsSplitLaunchAnimatorLegacy( @Nullable GroupedTaskView launchingTaskView, @NonNull Task initialTask, @NonNull Task secondTask, @NonNull RemoteAnimationTargetCompat[] appTargets, @Nullable GroupedTaskView launchingTaskView, int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, @NonNull RemoteAnimationTargetCompat[] nonAppTargets, @NonNull StateManager stateManager, Loading Loading @@ -478,7 +492,7 @@ public final class TaskViewUtils { if (mode == MODE_OPENING) { openingTargets.add(leash); } else if (taskId == initialTask.key.id || taskId == secondTask.key.id) { } else if (taskId == initialTaskId || taskId == secondTaskId) { throw new IllegalStateException("Expected task to be opening, but it is " + mode); } else if (mode == MODE_CLOSING) { closingTargets.add(leash); Loading
quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +7 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.popup.QuickstepSystemShortcut; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.FallbackActivityInterface; Loading Loading @@ -254,4 +255,10 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta // Do not let touch escape to siblings below this view. return result || mActivity.getStateManager().getState().overviewUi(); } @Override public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) { super.initiateSplitSelect(splitSelectSource); mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT); } }
quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +69 −41 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.quickstep.util; import static android.app.ActivityTaskManager.INVALID_TASK_ID; 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; Loading @@ -24,7 +26,8 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO import android.app.ActivityOptions; import android.app.ActivityThread; import android.graphics.Rect; import android.app.PendingIntent; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.view.RemoteAnimationAdapter; Loading @@ -42,7 +45,6 @@ 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.recents.model.Task; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; Loading @@ -62,16 +64,16 @@ public class SplitSelectStateController { private final StateManager mStateManager; private final DepthController mDepthController; private @StagePosition int mStagePosition; private Task mInitialTask; private Task mSecondTask; private PendingIntent mInitialTaskPendingIntent; private int mInitialTaskId = INVALID_TASK_ID; private int mSecondTaskId = INVALID_TASK_ID; 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, StateManager stateManager, DepthController depthController) { StateManager stateManager, DepthController depthController) { mHandler = handler; mSystemUiProxy = systemUiProxy; mStateManager = stateManager; Loading @@ -81,19 +83,26 @@ public class SplitSelectStateController { /** * To be called after first task selected */ public void setInitialTaskSelect(Task task, @StagePosition int stagePosition, Rect initialBounds) { mInitialTask = task; public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) { mInitialTaskId = taskId; mStagePosition = stagePosition; mInitialTaskPendingIntent = null; } public void setInitialTaskSelect(PendingIntent pendingIntent, @StagePosition int stagePosition) { mInitialTaskPendingIntent = pendingIntent; mStagePosition = stagePosition; mInitialTaskId = INVALID_TASK_ID; } /** * To be called after second task selected */ public void setSecondTaskId(Task task, Consumer<Boolean> callback) { mSecondTask = task; launchTasks(mInitialTask, mSecondTask, mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO); public void setSecondTaskId(int taskId, Consumer<Boolean> callback) { mSecondTaskId = taskId; launchTasks(mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO); } /** Loading @@ -104,7 +113,8 @@ public class SplitSelectStateController { mLaunchingTaskView = groupedTaskView; TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers = groupedTaskView.getTaskIdAttributeContainers(); launchTasks(taskIdAttributeContainers[0].getTask(), taskIdAttributeContainers[1].getTask(), launchTasks(taskIdAttributeContainers[0].getTask().key.id, null, taskIdAttributeContainers[1].getTask().key.id, taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList, groupedTaskView.getSplitRatio()); } Loading @@ -112,22 +122,25 @@ public class SplitSelectStateController { /** * @param stagePosition representing location of task1 */ public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition, Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) { public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent, int taskId2, @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}; ? new int[]{taskId1, taskId2} : new int[]{taskId2, taskId1}; if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { RemoteSplitLaunchTransitionRunner animationRunner = new RemoteSplitLaunchTransitionRunner(task1, task2); new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2); mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR, ActivityThread.currentActivityThread().getApplicationThread())); // TODO: handle intent + task with shell transition } else { RemoteSplitLaunchAnimationRunner animationRunner = new RemoteSplitLaunchAnimationRunner(task1, task2, callback); new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2, callback); final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner), 300, 150, Loading @@ -137,9 +150,16 @@ public class SplitSelectStateController { if (freezeTaskList) { mainOpts.setFreezeRecentTasksReordering(); } if (taskPendingIntent == null) { mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(), taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, adapter); } else { mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent, new Intent(), taskId2, stagePosition == STAGE_POSITION_TOP_OR_LEFT, mainOpts.toBundle(), null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, adapter); } } } Loading @@ -156,19 +176,22 @@ public class SplitSelectStateController { */ private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner { private final Task mInitialTask; private final Task mSecondTask; private final int mInitialTaskId; private final PendingIntent mInitialTaskPendingIntent; private final int mSecondTaskId; RemoteSplitLaunchTransitionRunner(Task initialTask, Task secondTask) { mInitialTask = initialTask; mSecondTask = secondTask; RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent, int secondTaskId) { mInitialTaskId = initialTaskId; mInitialTaskPendingIntent = initialTaskPendingIntent; mSecondTaskId = secondTaskId; } @Override public void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, Runnable finishCallback) { TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTask, mSecondTask, info, t, finishCallback); TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, info, t, finishCallback); // After successful launch, call resetState resetState(); } Loading @@ -180,14 +203,16 @@ public class SplitSelectStateController { */ private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat { private final Task mInitialTask; private final Task mSecondTask; private final int mInitialTaskId; private final PendingIntent mInitialTaskPendingIntent; private final int mSecondTaskId; private final Consumer<Boolean> mSuccessCallback; RemoteSplitLaunchAnimationRunner(Task initialTask, Task secondTask, Consumer<Boolean> successCallback) { mInitialTask = initialTask; mSecondTask = secondTask; RemoteSplitLaunchAnimationRunner(int initialTaskId, PendingIntent initialTaskPendingIntent, int secondTaskId, Consumer<Boolean> successCallback) { mInitialTaskId = initialTaskId; mInitialTaskPendingIntent = initialTaskPendingIntent; mSecondTaskId = secondTaskId; mSuccessCallback = successCallback; } Loading @@ -197,8 +222,9 @@ public class SplitSelectStateController { Runnable finishedCallback) { postAsyncCallback(mHandler, () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy( mLaunchingTaskView, mInitialTask, mSecondTask, apps, wallpapers, nonApps, mStateManager, mDepthController, () -> { mLaunchingTaskView, mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, apps, wallpapers, nonApps, mStateManager, mDepthController, () -> { finishedCallback.run(); if (mSuccessCallback != null) { mSuccessCallback.accept(true); Loading @@ -224,8 +250,9 @@ public class SplitSelectStateController { * To be called if split select was cancelled */ public void resetState() { mInitialTask = null; mSecondTask = null; mInitialTaskId = INVALID_TASK_ID; mInitialTaskPendingIntent = null; mSecondTaskId = INVALID_TASK_ID; mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; mRecentsAnimationRunning = false; mLaunchingTaskView = null; Loading @@ -236,6 +263,7 @@ public class SplitSelectStateController { * chosen */ public boolean isSplitSelectActive() { return mInitialTask != null && mSecondTask == null; return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskPendingIntent != null) && mSecondTaskId == INVALID_TASK_ID; } }