Let TaskView.bind take GroupTask instead of Task
1. Introduce a `groupTask` property to `TaskView` and let all the `bind` functions take a type of `GroupTask` as the parameter instead of a `Task`. 2. Then let `RecentsView.removeGroupTaskInternal` take the `GroupTask` as the parameter instead of a `TaskView`. 3. Caches the `groupTask` before `removeGroupTaskInternal` then we can avoid the corresponding `deskId` being set to invalid on `removeViewInLayout` in the main thread, which might happen before `removeGroupTaskInternal`. Bug: 408032417 Test: m Flag: EXEMPT refactor Change-Id: I51c5b810eb63bc1267b90b915f90164f7ca3cba5
This commit is contained in:
@@ -64,6 +64,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
|
||||
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
|
||||
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -1395,7 +1396,9 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
|
||||
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
|
||||
/* callback= */ success -> mSplitSelectStateController.resetState(),
|
||||
/* freezeTaskList= */ false,
|
||||
splitTask.getSplitBounds().snapPosition,
|
||||
splitTask.getSplitBounds() == null
|
||||
? SNAP_TO_2_50_50
|
||||
: splitTask.getSplitBounds().snapPosition,
|
||||
remoteTransition);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.wm.shell.Flags;
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.shared.GroupedTaskInfo;
|
||||
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
|
||||
import com.android.wm.shell.shared.split.SplitBounds;
|
||||
|
||||
import kotlin.collections.ArraysKt;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
@@ -429,9 +430,10 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
|
||||
final Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
|
||||
final Task task2 = Task.from(task2Key, taskInfo2,
|
||||
tmpLockedUsers.get(task2Key.userId) /* isLocked */);
|
||||
final SplitBounds splitBounds = rawTask.getBaseGroupedTask().getSplitBounds();
|
||||
final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
|
||||
convertShellSplitBoundsToLauncher(
|
||||
rawTask.getBaseGroupedTask().getSplitBounds());
|
||||
splitBounds == null ? null : convertShellSplitBoundsToLauncher(
|
||||
splitBounds);
|
||||
allTasks.add(new SplitTask(task1, task2, launcherSplitBounds));
|
||||
} else {
|
||||
allTasks.add(new SingleTask(task1));
|
||||
|
||||
@@ -94,11 +94,16 @@ class SingleTask(task: Task) : GroupTask(listOf(task), task.key.displayId, TaskV
|
||||
* A [Task] container that must contain exactly two tasks and split bounds to represent an app-pair
|
||||
* in the recent tasks list.
|
||||
*/
|
||||
class SplitTask(task1: Task, task2: Task, val splitBounds: SplitConfigurationOptions.SplitBounds) :
|
||||
class SplitTask(task1: Task, task2: Task, val splitBounds: SplitConfigurationOptions.SplitBounds?) :
|
||||
GroupTask(listOf(task1, task2), task1.key.displayId, TaskViewType.GROUPED) {
|
||||
|
||||
val topLeftTask: Task
|
||||
get() = if (splitBounds.leftTopTaskId == tasks[0].key.id) tasks[0] else tasks[1]
|
||||
get() =
|
||||
when {
|
||||
splitBounds == null -> tasks[0]
|
||||
splitBounds.leftTopTaskId == tasks[0].key.id -> tasks[0]
|
||||
else -> tasks[1]
|
||||
}
|
||||
|
||||
val bottomRightTask: Task
|
||||
get() = if (topLeftTask == tasks[0]) tasks[1] else tasks[0]
|
||||
|
||||
@@ -70,11 +70,12 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
type = TaskViewType.DESKTOP,
|
||||
thumbnailFullscreenParams = DesktopFullscreenDrawParams(context),
|
||||
) {
|
||||
private val desktopTask: DesktopTask?
|
||||
get() = groupTask as? DesktopTask
|
||||
|
||||
val deskId
|
||||
get() = desktopTask?.deskId ?: DesktopVisibilityController.INACTIVE_DESK_ID
|
||||
|
||||
private var desktopTask: DesktopTask? = null
|
||||
|
||||
private val contentViewFullscreenParams = FullscreenDrawParams(context)
|
||||
|
||||
private val taskThumbnailViewDeprecatedPool =
|
||||
@@ -298,7 +299,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
orientedState: RecentsOrientedState,
|
||||
taskOverlayFactory: TaskOverlayFactory,
|
||||
) {
|
||||
this.desktopTask = desktopTask
|
||||
this.groupTask = desktopTask
|
||||
// Minimized tasks are shown in Overview when exploded view is enabled.
|
||||
val tasks =
|
||||
if (enableDesktopExplodedView()) {
|
||||
@@ -364,7 +365,6 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
|
||||
override fun onRecycle() {
|
||||
super.onRecycle()
|
||||
desktopTask = null
|
||||
explodeProgress = 0.0f
|
||||
viewModel = null
|
||||
visibility = VISIBLE
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEF
|
||||
import com.android.quickstep.TaskOverlayFactory
|
||||
import com.android.quickstep.util.RecentsOrientedState
|
||||
import com.android.quickstep.util.SplitSelectStateController
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
import com.android.quickstep.util.SplitTask
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
|
||||
import com.android.wm.shell.Flags.enableFlexibleTwoAppSplit
|
||||
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition
|
||||
@@ -120,17 +120,16 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
}
|
||||
|
||||
fun bind(
|
||||
primaryTask: Task,
|
||||
secondaryTask: Task,
|
||||
splitTask: SplitTask,
|
||||
orientedState: RecentsOrientedState,
|
||||
taskOverlayFactory: TaskOverlayFactory,
|
||||
splitBoundsConfig: SplitConfigurationOptions.SplitBounds?,
|
||||
) {
|
||||
this.groupTask = splitTask
|
||||
cancelPendingLoadTasks()
|
||||
taskContainers =
|
||||
listOf(
|
||||
createTaskContainer(
|
||||
primaryTask,
|
||||
splitTask.topLeftTask,
|
||||
R.id.snapshot,
|
||||
R.id.icon,
|
||||
R.id.show_windows,
|
||||
@@ -139,7 +138,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
taskOverlayFactory,
|
||||
),
|
||||
createTaskContainer(
|
||||
secondaryTask,
|
||||
splitTask.bottomRightTask,
|
||||
R.id.bottomright_snapshot,
|
||||
R.id.bottomRight_icon,
|
||||
R.id.show_windows_right,
|
||||
@@ -148,7 +147,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
taskOverlayFactory,
|
||||
),
|
||||
)
|
||||
this.splitBoundsConfig = splitBoundsConfig
|
||||
this.splitBoundsConfig = splitTask.splitBounds
|
||||
taskContainers.forEach { it.digitalWellBeingToast?.splitBounds = splitBoundsConfig }
|
||||
onBind(orientedState)
|
||||
}
|
||||
|
||||
@@ -2040,10 +2040,7 @@ public abstract class RecentsView<
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.bind");
|
||||
if (taskView instanceof GroupedTaskView groupedTaskView) {
|
||||
var splitTask = (SplitTask) groupTask;
|
||||
groupedTaskView.bind(splitTask.getTopLeftTask(),
|
||||
splitTask.getBottomRightTask(), mOrientationState,
|
||||
mTaskOverlayFactory, splitTask.getSplitBounds());
|
||||
groupedTaskView.bind((SplitTask) groupTask, mOrientationState, mTaskOverlayFactory);
|
||||
} else if (taskView instanceof DesktopTaskView desktopTaskView) {
|
||||
desktopTaskView.bind((DesktopTask) groupTask, mOrientationState,
|
||||
mTaskOverlayFactory);
|
||||
@@ -2051,10 +2048,9 @@ public abstract class RecentsView<
|
||||
Task task = splitTask.getTopLeftTask().key.id == stagedTaskIdToBeRemoved
|
||||
? splitTask.getBottomRightTask()
|
||||
: splitTask.getTopLeftTask();
|
||||
taskView.bind(task, mOrientationState, mTaskOverlayFactory);
|
||||
taskView.bind(new SingleTask(task), mOrientationState, mTaskOverlayFactory);
|
||||
} else {
|
||||
taskView.bind(((SingleTask) groupTask).getTask(), mOrientationState,
|
||||
mTaskOverlayFactory);
|
||||
taskView.bind((SingleTask) groupTask, mOrientationState, mTaskOverlayFactory);
|
||||
}
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.addTaskView");
|
||||
@@ -3042,13 +3038,14 @@ public abstract class RecentsView<
|
||||
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
|
||||
// the actual app running we won't need to show the thumbnail until all the tasks
|
||||
// load later anyways
|
||||
((GroupedTaskView) taskView).bind(Task.from(groupedTaskInfo.getTaskInfo1()),
|
||||
Task.from(groupedTaskInfo.getTaskInfo2()), mOrientationState,
|
||||
mTaskOverlayFactory, mSplitBoundsConfig);
|
||||
((GroupedTaskView) taskView).bind(
|
||||
new SplitTask(Task.from(groupedTaskInfo.getTaskInfo1()),
|
||||
Task.from(groupedTaskInfo.getTaskInfo2()), mSplitBoundsConfig),
|
||||
mOrientationState, mTaskOverlayFactory);
|
||||
} else {
|
||||
taskView = getTaskViewFromPool(TaskViewType.SINGLE);
|
||||
taskView.bind(Task.from(groupedTaskInfo.getTaskInfo1()), mOrientationState,
|
||||
mTaskOverlayFactory);
|
||||
taskView.bind(new SingleTask(Task.from(groupedTaskInfo.getTaskInfo1())),
|
||||
mOrientationState, mTaskOverlayFactory);
|
||||
}
|
||||
if (mAddDesktopButton != null && wasEmpty) {
|
||||
addView(mAddDesktopButton);
|
||||
@@ -4036,12 +4033,17 @@ public abstract class RecentsView<
|
||||
|
||||
if (success) {
|
||||
mAnyTaskHasBeenDismissed = true;
|
||||
if (shouldRemoveTask && dismissedTaskView != null) {
|
||||
// Caches the [groupTask] before removing it. As the [deskId] might become
|
||||
// invalid by [removeViewInLayout] on the [dismissedTaskView] below. It might
|
||||
// happen before [removeGroupTaskInternal] that runs on a helper thread.
|
||||
final GroupTask groupTask;
|
||||
if (shouldRemoveTask && dismissedTaskView != null
|
||||
&& (groupTask = dismissedTaskView.getGroupTask()) != null) {
|
||||
if (dismissedTaskView.isRunningTask()) {
|
||||
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
|
||||
() -> removeTaskInternal(dismissedTaskView));
|
||||
() -> removeGroupTaskInternal(groupTask));
|
||||
} else {
|
||||
removeTaskInternal(dismissedTaskView);
|
||||
removeGroupTaskInternal(groupTask);
|
||||
}
|
||||
mContainer.getStatsLogManager().logger()
|
||||
.withItemInfo(dismissedTaskView.getItemInfo())
|
||||
@@ -4383,34 +4385,31 @@ public abstract class RecentsView<
|
||||
return lastVisibleTaskView;
|
||||
}
|
||||
|
||||
private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
|
||||
private void removeGroupTaskInternal(@NonNull GroupTask groupTask) {
|
||||
UI_HELPER_EXECUTOR
|
||||
.getHandler()
|
||||
.post(
|
||||
() -> {
|
||||
if (dismissedTaskView instanceof DesktopTaskView desktopTaskView) {
|
||||
removeDesktopTaskView(desktopTaskView);
|
||||
if (groupTask instanceof DesktopTask desktopTask) {
|
||||
if (areMultiDesksFlagsEnabled()) {
|
||||
SystemUiProxy.INSTANCE
|
||||
.get(getContext())
|
||||
.removeDesk(desktopTask.getDeskId());
|
||||
} else if (DesktopModeFlags
|
||||
.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
|
||||
SystemUiProxy.INSTANCE
|
||||
.get(getContext())
|
||||
.removeDefaultDeskInDisplay(
|
||||
mContainer.getDisplay().getDisplayId());
|
||||
}
|
||||
} else {
|
||||
for (int taskId : dismissedTaskView.getTaskIds()) {
|
||||
ActivityManagerWrapper.getInstance().removeTask(taskId);
|
||||
for (Task task : groupTask.getTasks()) {
|
||||
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeDesktopTaskView(DesktopTaskView desktopTaskView) {
|
||||
if (areMultiDesksFlagsEnabled()) {
|
||||
SystemUiProxy.INSTANCE
|
||||
.get(getContext())
|
||||
.removeDesk(desktopTaskView.getDeskId());
|
||||
} else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
|
||||
SystemUiProxy.INSTANCE
|
||||
.get(getContext())
|
||||
.removeDefaultDeskInDisplay(
|
||||
mContainer.getDisplay().getDisplayId());
|
||||
}
|
||||
}
|
||||
|
||||
protected void onDismissAnimationEnds() {
|
||||
AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
|
||||
DISMISS_ANIMATION_ENDS_MESSAGE);
|
||||
|
||||
@@ -92,7 +92,9 @@ import com.android.quickstep.util.ActiveGestureErrorDetector
|
||||
import com.android.quickstep.util.ActiveGestureLog
|
||||
import com.android.quickstep.util.BorderAnimator
|
||||
import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator
|
||||
import com.android.quickstep.util.GroupTask
|
||||
import com.android.quickstep.util.RecentsOrientedState
|
||||
import com.android.quickstep.util.SingleTask
|
||||
import com.android.quickstep.util.TaskCornerRadius
|
||||
import com.android.quickstep.util.TaskRemovedDuringLaunchListener
|
||||
import com.android.quickstep.util.isExternalDisplay
|
||||
@@ -131,6 +133,8 @@ constructor(
|
||||
@IntDef(FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL, FLAG_UPDATE_CORNER_RADIUS)
|
||||
annotation class TaskDataChanges
|
||||
|
||||
var groupTask: GroupTask? = null
|
||||
|
||||
val taskIds: IntArray
|
||||
/** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
|
||||
get() = taskContainers.map { it.task.key.id }.toIntArray()
|
||||
@@ -692,6 +696,7 @@ constructor(
|
||||
override fun onRecycle() {
|
||||
resetPersistentViewTransforms()
|
||||
|
||||
groupTask = null
|
||||
// Bind ViewModel to no taskIds
|
||||
viewModel?.bind()
|
||||
attachAlpha = 1f
|
||||
@@ -934,16 +939,17 @@ constructor(
|
||||
|
||||
/** Updates this task view to the given {@param task}. */
|
||||
open fun bind(
|
||||
task: Task,
|
||||
singleTask: SingleTask,
|
||||
orientedState: RecentsOrientedState,
|
||||
taskOverlayFactory: TaskOverlayFactory,
|
||||
) {
|
||||
this.groupTask = singleTask
|
||||
cancelPendingLoadTasks()
|
||||
this.orientedState = orientedState // Needed for dependencies
|
||||
taskContainers =
|
||||
listOf(
|
||||
createTaskContainer(
|
||||
task,
|
||||
singleTask.task,
|
||||
R.id.snapshot,
|
||||
R.id.icon,
|
||||
R.id.show_windows,
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.quickstep.orientation.LandscapePagedViewHandler
|
||||
import com.android.quickstep.recents.di.RecentsDependencies
|
||||
import com.android.quickstep.task.thumbnail.TaskThumbnailView
|
||||
import com.android.quickstep.util.RecentsOrientedState
|
||||
import com.android.quickstep.util.SingleTask
|
||||
import com.android.quickstep.views.LauncherRecentsView
|
||||
import com.android.quickstep.views.RecentsViewContainer
|
||||
import com.android.quickstep.views.TaskContainer
|
||||
@@ -165,7 +166,7 @@ class AspectRatioSystemShortcutTests {
|
||||
val taskContainer = createTaskContainer(task)
|
||||
|
||||
setScreenSizeDp(widthDp = 1200, heightDp = 800)
|
||||
taskView.bind(task, orientedState, taskOverlayFactory)
|
||||
taskView.bind(SingleTask(task), orientedState, taskOverlayFactory)
|
||||
|
||||
assertThat(factory.getShortcuts(launcher, taskContainer)).isNull()
|
||||
}
|
||||
@@ -181,7 +182,7 @@ class AspectRatioSystemShortcutTests {
|
||||
val taskContainer = createTaskContainer(task)
|
||||
|
||||
setScreenSizeDp(widthDp = 599, heightDp = 599)
|
||||
taskView.bind(task, orientedState, taskOverlayFactory)
|
||||
taskView.bind(SingleTask(task), orientedState, taskOverlayFactory)
|
||||
|
||||
assertThat(factory.getShortcuts(launcher, taskContainer)).isNull()
|
||||
}
|
||||
@@ -199,7 +200,7 @@ class AspectRatioSystemShortcutTests {
|
||||
doReturn(taskViewItemInfo).whenever(taskContainer).itemInfo
|
||||
|
||||
setScreenSizeDp(widthDp = 1200, heightDp = 800)
|
||||
taskView.bind(task, orientedState, taskOverlayFactory)
|
||||
taskView.bind(SingleTask(task), orientedState, taskOverlayFactory)
|
||||
|
||||
val shortcuts = factory.getShortcuts(launcher, taskContainer)
|
||||
assertThat(shortcuts).hasSize(1)
|
||||
|
||||
Reference in New Issue
Block a user