Merge "Use already-running app instances when splitting" into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index c65fa5f..9554bd3 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -30,6 +30,8 @@
import android.util.Log;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
@@ -41,6 +43,9 @@
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.function.Consumer;
public interface QuickstepSystemShortcut {
@@ -93,14 +98,22 @@
}
StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
- SplitSelectSource source = new SplitSelectSource(mOriginalView,
- new BitmapDrawable(bitmap), intent, mPosition, mItemInfo, splitEvent);
- if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
- startSplitToHome(source);
- } else {
- RecentsView recentsView = mTarget.getOverviewPanel();
- recentsView.initiateSplitSelect(source);
- }
+ RecentsView recentsView = mTarget.getOverviewPanel();
+ // Check if there is already an instance of this app running, if so, initiate the split
+ // using that.
+ recentsView.findLastActiveTaskAndDoSplitOperation(
+ intent.getComponent(),
+ (Consumer<Task>) foundTask -> {
+ SplitSelectSource source = new SplitSelectSource(mOriginalView,
+ new BitmapDrawable(bitmap), intent, mPosition, mItemInfo,
+ splitEvent, foundTask);
+ if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ startSplitToHome(source);
+ } else {
+ recentsView.initiateSplitSelect(source);
+ }
+ }
+ );
}
private void startSplitToHome(SplitSelectSource source) {
@@ -108,7 +121,7 @@
SplitSelectStateController controller = mTarget.getSplitSelectStateController();
controller.setInitialTaskSelect(source.intent, source.position.stagePosition,
- source.itemInfo, source.splitEvent);
+ source.itemInfo, source.splitEvent, source.alreadyRunningTask);
RecentsView recentsView = mTarget.getOverviewPanel();
recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
@@ -142,16 +155,19 @@
public final SplitPositionOption position;
public final ItemInfo itemInfo;
public final StatsLogManager.EventEnum splitEvent;
+ @Nullable
+ public final Task alreadyRunningTask;
public SplitSelectSource(View view, Drawable drawable, Intent intent,
SplitPositionOption position, ItemInfo itemInfo,
- StatsLogManager.EventEnum splitEvent) {
+ StatsLogManager.EventEnum splitEvent, @Nullable Task foundTask) {
this.view = view;
this.drawable = drawable;
this.intent = intent;
this.position = position;
this.itemInfo = itemInfo;
this.splitEvent = splitEvent;
+ this.alreadyRunningTask = foundTask;
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index b3b53c2..d8e1311 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -28,8 +28,10 @@
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* Base class for providing different taskbar UI
@@ -160,23 +162,30 @@
*/
public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
RecentsView recents = getRecentsView();
- TaskView foundTaskView = recents.getTaskViewByComponentName(info.getTargetComponent());
- if (foundTaskView != null) {
- recents.confirmSplitSelect(
- foundTaskView,
- foundTaskView.getTask(),
- foundTaskView.getIconView().getDrawable(),
- foundTaskView.getThumbnail(),
- foundTaskView.getThumbnail().getThumbnail(),
- /* intent */ null);
- } else {
- recents.confirmSplitSelect(
- /* containerTaskView */ null,
- /* task */ null,
- new BitmapDrawable(info.bitmap.icon),
- startingView,
- /* thumbnail */ null,
- intent);
- }
+ recents.findLastActiveTaskAndDoSplitOperation(
+ info.getTargetComponent(),
+ (Consumer<Task>) foundTask -> {
+ if (foundTask != null) {
+ TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
+ // There is already a running app of this type, use that as second app.
+ recents.confirmSplitSelect(
+ foundTaskView,
+ foundTaskView.getTask(),
+ foundTaskView.getIconView().getDrawable(),
+ foundTaskView.getThumbnail(),
+ foundTaskView.getThumbnail().getThumbnail(),
+ null /* intent */);
+ } else {
+ // No running app of that type, create a new instance as second app.
+ recents.confirmSplitSelect(
+ null /* containerTaskView */,
+ null /* task */,
+ new BitmapDrawable(info.bitmap.icon),
+ startingView,
+ null /* thumbnail */,
+ intent);
+ }
+ }
+ );
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 3053474..3d6da8e 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -96,7 +96,8 @@
}
/**
- * Fetches the list of recent tasks.
+ * Fetches the list of recent tasks. Tasks are ordered by recency, with the latest active tasks
+ * at the end of the list.
*
* @param callback The callback to receive the task plan once its complete or null. This is
* always called on the UI thread.
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index c263fe8..681f068 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -37,7 +37,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.RemoteAnimationAdapter;
@@ -124,10 +123,15 @@
* To be called after first task selected from home or all apps.
*/
public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
- @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
- mInitialTaskIntent = intent;
- mUser = itemInfo.user;
- mItemInfo = itemInfo;
+ @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent,
+ @Nullable Task alreadyRunningTask) {
+ if (alreadyRunningTask != null) {
+ mInitialTaskId = alreadyRunningTask.key.id;
+ } else {
+ mInitialTaskIntent = intent;
+ mUser = itemInfo.user;
+ }
+
setInitialData(stagePosition, splitEvent, itemInfo);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 0e49769..3a2841e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1221,18 +1221,50 @@
}
/**
- * Returns a {@link TaskView} that has ComponentName matching {@code componentName} or null if
- * no match.
+ * Pulls the list of active Tasks from RecentModel, and finds the most recently active Task
+ * matching a given ComponentName. Then uses that Task (which could be null) with the given
+ * callback.
+ *
+ * Used in various splitscreen operations when we need to check if there is a currently running
+ * Task of a certain type and use the most recent one.
*/
- @Nullable
- public TaskView getTaskViewByComponentName(ComponentName componentName) {
- for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView taskView = requireTaskViewAt(i);
- if (taskView.getTask().key.sourceComponent.equals(componentName)) {
- return taskView;
+ public void findLastActiveTaskAndDoSplitOperation(ComponentName componentName,
+ Consumer<Task> callback) {
+ mModel.getTasks(taskGroups -> {
+ Task lastActiveTask = null;
+ // Loop through tasks in reverse, since they are ordered with most-recent tasks last.
+ for (int i = taskGroups.size() - 1; i >= 0; i--) {
+ GroupTask groupTask = taskGroups.get(i);
+ Task task1 = groupTask.task1;
+ if (isInstanceOfComponent(task1, componentName)) {
+ lastActiveTask = task1;
+ break;
+ }
+ Task task2 = groupTask.task2;
+ if (isInstanceOfComponent(task2, componentName)) {
+ lastActiveTask = task2;
+ break;
+ }
}
+
+ callback.accept(lastActiveTask);
+ });
+ }
+
+ /**
+ * Checks if a given Task is the most recently-active Task of type componentName. Used for
+ * selecting already-running Tasks for splitscreen.
+ */
+ public boolean isInstanceOfComponent(@Nullable Task task, ComponentName componentName) {
+ if (task == null) {
+ return false;
}
- return null;
+ // Exclude the task that is already staged
+ if (mSplitHiddenTaskView != null && mSplitHiddenTaskView.getTask().equals(task)) {
+ return false;
+ }
+
+ return task.key.baseIntent.getComponent().equals(componentName);
}
public void setOverviewStateEnabled(boolean enabled) {
@@ -1509,13 +1541,41 @@
int previousCurrentPage = mCurrentPage;
removeAllViews();
- // Add views as children based on whether it's grouped or single task
+ // If we are entering Overview as a result of initiating a split from somewhere else
+ // (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
+ Task stagedTaskToBeRemovedFromGrid =
+ mSplitSelectSource != null ? mSplitSelectSource.alreadyRunningTask : null;
+
+ // Add views as children based on whether it's grouped or single task. Looping through
+ // taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);
+ boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null
+ && groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id);
+
+ TaskView taskView;
+ if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
+ // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
+ // to be a temporary container for the remaining task.
+ taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
+ } else {
+ taskView = getTaskViewFromPool(groupTask.taskViewType);
+ }
+
addView(taskView);
- if (taskView instanceof GroupedTaskView) {
+ if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
+ if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
+ taskView.bind(groupTask.task2, mOrientationState);
+ } else {
+ taskView.bind(groupTask.task1, mOrientationState);
+ }
+ } else if (isRemovalNeeded) {
+ // If the task we need to remove is not part of a pair, bind it to the TaskView
+ // first (to prevent problems), then remove the whole thing.
+ taskView.bind(groupTask.task1, mOrientationState);
+ removeView(taskView);
+ } else if (taskView instanceof GroupedTaskView) {
boolean firstTaskIsLeftTopTask =
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
@@ -4269,7 +4329,7 @@
mSplitSelectSource = splitSelectSource;
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
- splitSelectSource.splitEvent);
+ splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTask);
}
/**