Only stabilize tasks during quick switch session
- A stabilization session starts when going to a new task via a quick switch
- A stabilization session ends after 5 seconds on the same task or when completing
a gesture to go to home or overview
- We track the system task order and copy it for the duration of the stabilization session
- After the session ends, we switch back to the system task order
- Remove ENABLE_TASK_STABILIZATION flag (and also QUICK_SWITCH)
Bug: 111926330
Change-Id: I2c18dff2d20ad02b73a4935ac534e9fc9f5f49c0
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index cedd952..00b3e90 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -73,6 +73,14 @@
});
}
+ public void startStabilizationSession() {
+ mStabilizer.startStabilizationSession();
+ }
+
+ public void endStabilizationSession() {
+ mStabilizer.endStabilizationSession();
+ }
+
/**
* Asynchronously fetches the list of recent tasks, reusing cached list if available.
*
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index e61c00a..57694d5 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep;
-import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
@@ -28,6 +26,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
+
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
@@ -35,11 +34,14 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+
import java.util.ArrayList;
import java.util.function.Consumer;
import androidx.annotation.WorkerThread;
+import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+
/**
* Singleton class to load and manage recents model.
*/
@@ -90,6 +92,14 @@
return mThumbnailCache;
}
+ public void startStabilizationSession() {
+ mTaskList.startStabilizationSession();
+ }
+
+ public void endStabilizationSession() {
+ mTaskList.endStabilizationSession();
+ }
+
/**
* Fetches the list of recent tasks.
*
diff --git a/quickstep/src/com/android/quickstep/TaskListStabilizer.java b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
index 3eb26b4..4c63f81 100644
--- a/quickstep/src/com/android/quickstep/TaskListStabilizer.java
+++ b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
@@ -15,13 +15,10 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
-
import android.app.ActivityManager.RecentTaskInfo;
import android.content.ComponentName;
import android.os.Process;
import android.os.SystemClock;
-import android.util.Log;
import com.android.launcher3.util.IntArray;
import com.android.systemui.shared.recents.model.Task;
@@ -33,98 +30,77 @@
import java.util.Collections;
import java.util.List;
+/**
+ * Keeps the task list stable during quick switch gestures. So if you swipe right to switch from app
+ * A to B, you can then swipe right again to get to app C or left to get back to A.
+ */
public class TaskListStabilizer {
private static final long TASK_CACHE_TIMEOUT_MS = 5000;
- private static final int INVALID_TASK_ID = -1;
-
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskCreated(int taskId, ComponentName componentName) {
- onTaskCreatedInternal(taskId);
- }
-
- @Override
- public void onTaskMovedToFront(int taskId) {
- onTaskMovedToFrontInternal(taskId);
+ endStabilizationSession();
}
@Override
public void onTaskRemoved(int taskId) {
- onTaskRemovedInternal(taskId);
+ endStabilizationSession();
}
};
- // Task ids ordered based on recency, 0th index is the latest task
- private final IntArray mOrderedTaskIds;
+ // Task ids ordered based on recency, 0th index is the least recent task
+ private final IntArray mSystemOrder;
+ private final IntArray mStabilizedOrder;
// Wrapper objects used for sorting tasks
private final ArrayList<TaskWrapper> mTaskWrappers = new ArrayList<>();
- // Information about recent task re-order which has not been applied yet
- private int mScheduledMoveTaskId = INVALID_TASK_ID;
- private long mScheduledMoveTime = 0;
+ private boolean mInStabilizationSession;
+ private long mSessionStartTime;
public TaskListStabilizer() {
- if (ENABLE_TASK_STABILIZER.get()) {
- // Initialize the task ids map
- List<RecentTaskInfo> rawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
- Integer.MAX_VALUE, Process.myUserHandle().getIdentifier());
- mOrderedTaskIds = new IntArray(rawTasks.size());
- for (RecentTaskInfo info : rawTasks) {
- mOrderedTaskIds.add(new TaskKey(info).id);
- }
-
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
- } else {
- mOrderedTaskIds = null;
+ // Initialize the task ids map
+ List<RecentTaskInfo> rawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
+ Integer.MAX_VALUE, Process.myUserHandle().getIdentifier());
+ mSystemOrder = new IntArray(rawTasks.size());
+ for (RecentTaskInfo info : rawTasks) {
+ mSystemOrder.add(new TaskKey(info).id);
}
+ // We will lazily copy the task id's from mSystemOrder when a stabilization session starts.
+ mStabilizedOrder = new IntArray(rawTasks.size());
+
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
}
- private synchronized void onTaskCreatedInternal(int taskId) {
- applyScheduledMoveUnchecked();
- mOrderedTaskIds.add(taskId);
- }
-
- private synchronized void onTaskRemovedInternal(int taskId) {
- applyScheduledMoveUnchecked();
- mOrderedTaskIds.removeValue(taskId);
- }
-
- private void applyScheduledMoveUnchecked() {
- if (mScheduledMoveTaskId != INVALID_TASK_ID) {
- // Mode the scheduled task to front
- mOrderedTaskIds.removeValue(mScheduledMoveTaskId);
- mOrderedTaskIds.add(mScheduledMoveTaskId);
- mScheduledMoveTaskId = INVALID_TASK_ID;
+ public synchronized void startStabilizationSession() {
+ if (!mInStabilizationSession) {
+ mStabilizedOrder.clear();
+ mStabilizedOrder.addAll(mSystemOrder);
}
+ mInStabilizationSession = true;
+ mSessionStartTime = SystemClock.uptimeMillis();
}
- /**
- * Checks if the scheduled move has timed out and moves the task to front accordingly.
- */
- private void applyScheduledMoveIfTime() {
- if (mScheduledMoveTaskId != INVALID_TASK_ID
- && (SystemClock.uptimeMillis() - mScheduledMoveTime) > TASK_CACHE_TIMEOUT_MS) {
- applyScheduledMoveUnchecked();
- }
+ public synchronized void endStabilizationSession() {
+ mInStabilizationSession = false;
}
- private synchronized void onTaskMovedToFrontInternal(int taskId) {
- applyScheduledMoveIfTime();
- mScheduledMoveTaskId = taskId;
- mScheduledMoveTime = SystemClock.uptimeMillis();
- }
-
-
public synchronized ArrayList<Task> reorder(ArrayList<Task> tasks) {
- if (!ENABLE_TASK_STABILIZER.get()) {
- return tasks;
+ mSystemOrder.clear();
+ for (Task task : tasks) {
+ mSystemOrder.add(task.key.id);
}
- applyScheduledMoveIfTime();
+ if ((SystemClock.uptimeMillis() - mSessionStartTime) > TASK_CACHE_TIMEOUT_MS) {
+ endStabilizationSession();
+ }
+
+ if (!mInStabilizationSession) {
+ return tasks;
+ }
// Ensure that we have enough wrappers
int taskCount = tasks.size();
@@ -139,7 +115,7 @@
for (int i = 0; i < taskCount; i++){
TaskWrapper wrapper = listToSort.get(i);
wrapper.task = tasks.get(i);
- wrapper.index = mOrderedTaskIds.indexOf(wrapper.task.key.id);
+ wrapper.index = mStabilizedOrder.indexOf(wrapper.task.key.id);
// Ensure that missing tasks are put in the front, in the order they appear in the
// original list
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index aeb648d..e8c47e6 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -200,11 +200,15 @@
this.containerType = containerType;
}
- // 0 is app, 1 is overview
+ /** 0 is app, 1 is overview */
public final float endShift;
+ /** The state to apply when we reach this final target */
public final int endState;
+ /** Whether the target is in the launcher activity */
public final boolean isLauncher;
+ /** Whether the user can start a new gesture while this one is finishing */
public final boolean canBeContinued;
+ /** Used to log where the user ended up after the gesture ends */
public final int containerType;
}
@@ -926,6 +930,17 @@
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
mGestureEndTarget = target;
+
+ if (mGestureEndTarget.canBeContinued) {
+ // Because we might continue this gesture, e.g. for consecutive quick switch, we need to
+ // stabilize the task list so that tasks don't rearrange in the middle of the gesture.
+ RecentsModel.INSTANCE.get(mContext).startStabilizationSession();
+ } else if (mGestureEndTarget.isLauncher) {
+ // Otherwise, if we're going to home or overview,
+ // we reset the tasks to a consistent start state.
+ RecentsModel.INSTANCE.get(mContext).endStabilizationSession();
+ }
+
HomeAnimationFactory homeAnimFactory;
Animator windowAnim;
if (mGestureEndTarget == HOME) {
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
index 54038d2..656d55c 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
@@ -26,7 +26,6 @@
@Test
public void withFlagOn() {
assertTrue(FeatureFlags.EXAMPLE_FLAG.get());
- assertFalse(FeatureFlags.QUICK_SWITCH.get());
assertFalse(FeatureFlags.STYLE_WALLPAPER.get());
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index b921d29..61467e0 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -88,10 +88,6 @@
// trying to make them fit the orientation the device is in.
public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
- public static final ToggleableGlobalSettingsFlag QUICK_SWITCH
- = new ToggleableGlobalSettingsFlag("QUICK_SWITCH", false,
- "Swiping right on the nav bar while in an app switches to the previous app");
-
public static final ToggleableGlobalSettingsFlag STYLE_WALLPAPER
= new ToggleableGlobalSettingsFlag("STYLE_WALLPAPER", false,
"Direct users to the new ThemePicker based WallpaperPicker");
@@ -102,10 +98,6 @@
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
- public static final ToggleableGlobalSettingsFlag ENABLE_TASK_STABILIZER
- = new ToggleableGlobalSettingsFlag("ENABLE_TASK_STABILIZER", false,
- "Stable task list across fast task switches");
-
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
false, "Enable springs for quickstep animations");