1 package com.android.quickstep.util; 2 3 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; 4 5 import android.content.Context; 6 import android.os.IBinder; 7 8 import com.android.launcher3.util.MainThreadInitializedObject; 9 import com.android.launcher3.util.SplitConfigurationOptions; 10 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; 11 import com.android.launcher3.util.SplitConfigurationOptions.StageType; 12 import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitTaskPosition; 13 import com.android.quickstep.SystemUiProxy; 14 import com.android.systemui.shared.system.TaskStackChangeListener; 15 import com.android.systemui.shared.system.TaskStackChangeListeners; 16 import com.android.wm.shell.splitscreen.ISplitScreenListener; 17 18 /** 19 * Listeners for system wide split screen position and stage changes. 20 * 21 * Use {@link #getRunningSplitTaskIds()} to determine which tasks, if any, are actively in 22 * staged split. 23 * 24 * Use {@link #getPersistentSplitIds()} to know if tasks were in split screen before a quickswitch 25 * gesture happened. 26 */ 27 public class LauncherSplitScreenListener extends ISplitScreenListener.Stub { 28 29 public static final MainThreadInitializedObject<LauncherSplitScreenListener> INSTANCE = 30 new MainThreadInitializedObject<>(LauncherSplitScreenListener::new); 31 32 private static final int[] EMPTY_ARRAY = {}; 33 34 private final StagedSplitTaskPosition mMainStagePosition = new StagedSplitTaskPosition(); 35 private final StagedSplitTaskPosition mSideStagePosition = new StagedSplitTaskPosition(); 36 37 private boolean mIsRecentsListFrozen = false; 38 private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { 39 @Override 40 public void onRecentTaskListFrozenChanged(boolean frozen) { 41 super.onRecentTaskListFrozenChanged(frozen); 42 mIsRecentsListFrozen = frozen; 43 44 if (frozen) { 45 mPersistentGroupedIds = getRunningSplitTaskIds(); 46 } else { 47 mPersistentGroupedIds = EMPTY_ARRAY; 48 } 49 } 50 }; 51 52 /** 53 * Gets set to current split taskIDs whenever the task list is frozen, and set to empty array 54 * whenever task list unfreezes. This also gets set to empty array whenever the user swipes to 55 * home - in that case the task list does not unfreeze immediately after the gesture, so it's 56 * done via {@link #notifySwipingToHome()}. 57 * 58 * When not empty, this indicates that we need to load a GroupedTaskView as the most recent 59 * page, so user can quickswitch back to a grouped task. 60 */ 61 private int[] mPersistentGroupedIds; 62 LauncherSplitScreenListener(Context context)63 public LauncherSplitScreenListener(Context context) { 64 mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN; 65 mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE; 66 } 67 68 /** Also call {@link #destroy()} when done. */ init()69 public void init() { 70 SystemUiProxy.INSTANCE.getNoCreate().registerSplitScreenListener(this); 71 TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener); 72 } 73 destroy()74 public void destroy() { 75 SystemUiProxy.INSTANCE.getNoCreate().unregisterSplitScreenListener(this); 76 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); 77 } 78 79 /** 80 * This method returns the active split taskIDs that were active if a user quickswitched from 81 * split screen to a fullscreen app as long as the recents task list remains frozen. 82 */ getPersistentSplitIds()83 public int[] getPersistentSplitIds() { 84 if (mIsRecentsListFrozen) { 85 return mPersistentGroupedIds; 86 } else { 87 return getRunningSplitTaskIds(); 88 } 89 } 90 /** 91 * @return index 0 will be task in left/top position, index 1 in right/bottom position. 92 * Will return empty array if device is not in staged split 93 */ getRunningSplitTaskIds()94 public int[] getRunningSplitTaskIds() { 95 if (mMainStagePosition.taskId == -1 || mSideStagePosition.taskId == -1) { 96 return new int[]{}; 97 } 98 int[] out = new int[2]; 99 if (mMainStagePosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) { 100 out[0] = mMainStagePosition.taskId; 101 out[1] = mSideStagePosition.taskId; 102 } else { 103 out[1] = mMainStagePosition.taskId; 104 out[0] = mSideStagePosition.taskId; 105 } 106 return out; 107 } 108 109 @Override onStagePositionChanged(@tageType int stage, @StagePosition int position)110 public void onStagePositionChanged(@StageType int stage, @StagePosition int position) { 111 if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) { 112 mMainStagePosition.stagePosition = position; 113 } else { 114 mSideStagePosition.stagePosition = position; 115 } 116 } 117 118 @Override onTaskStageChanged(int taskId, @StageType int stage, boolean visible)119 public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) { 120 // If task is not visible but we are tracking it, stop tracking it 121 if (!visible) { 122 if (mMainStagePosition.taskId == taskId) { 123 resetTaskId(mMainStagePosition); 124 } else if (mSideStagePosition.taskId == taskId) { 125 resetTaskId(mSideStagePosition); 126 } // else it's an un-tracked child 127 return; 128 } 129 130 // If stage has moved to undefined, stop tracking the task 131 if (stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) { 132 resetTaskId(taskId == mMainStagePosition.taskId ? 133 mMainStagePosition : mSideStagePosition); 134 return; 135 } 136 137 if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) { 138 mMainStagePosition.taskId = taskId; 139 } else { 140 mSideStagePosition.taskId = taskId; 141 } 142 } 143 144 /** Notifies SystemUi to remove any split screen state */ notifySwipingToHome()145 public void notifySwipingToHome() { 146 boolean hasSplitTasks = LauncherSplitScreenListener.INSTANCE.getNoCreate() 147 .getPersistentSplitIds().length > 0; 148 if (!hasSplitTasks) { 149 return; 150 } 151 152 mPersistentGroupedIds = EMPTY_ARRAY; 153 } 154 resetTaskId(StagedSplitTaskPosition taskPosition)155 private void resetTaskId(StagedSplitTaskPosition taskPosition) { 156 taskPosition.taskId = -1; 157 } 158 159 @Override asBinder()160 public IBinder asBinder() { 161 return this; 162 } 163 } 164