Remove all external usages of GroupTask.task1/2 from Launcher3
See go/refactor-group-task for details. This CL removes all the usages of `task1` and `task2` from `GroupTask` in Launcher3. Follow-up CLs will remove it from NexusLauncher and remove the fields altogether. Bug: 388593902 Test: m Flag: EXEMPT pure refactor with no behavior change. Change-Id: I902c8135b3a0aae95acf25267b3bcbf286bd4e7d
This commit is contained in:
@@ -233,7 +233,8 @@ public final class KeyboardQuickSwitchController implements
|
||||
}
|
||||
|
||||
private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
|
||||
return Flags.taskbarOverflow() && taskIdsToExclude.contains(task.task1.key.id);
|
||||
return Flags.taskbarOverflow() && task.getTasks().stream().anyMatch(
|
||||
t -> taskIdsToExclude.contains(t.key.id));
|
||||
}
|
||||
|
||||
private void processLoadedTasks(List<GroupTask> tasks, Set<Integer> taskIdsToExclude) {
|
||||
|
||||
@@ -51,6 +51,9 @@ import com.android.launcher3.anim.AnimatedFloat;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.shared.TestProtocol;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.SingleTask;
|
||||
import com.android.quickstep.util.SplitTask;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -255,14 +258,21 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
|
||||
layoutInflater,
|
||||
previousTaskView);
|
||||
|
||||
final boolean firstTaskIsLeftTopTask =
|
||||
groupTask.mSplitBounds == null
|
||||
|| groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id
|
||||
|| groupTask.task2 == null;
|
||||
Task task1;
|
||||
Task task2;
|
||||
if (groupTask instanceof SplitTask splitTask) {
|
||||
task1 = splitTask.getTopLeftTask();
|
||||
task2 = splitTask.getBottomRightTask();
|
||||
} else if (groupTask instanceof SingleTask singleTask) {
|
||||
task1 = singleTask.getTask();
|
||||
task2 = null;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
currentTaskView.setThumbnailsForSplitTasks(
|
||||
firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2,
|
||||
firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1,
|
||||
task1,
|
||||
task2,
|
||||
updateTasks ? mViewCallbacks::updateThumbnailInBackground : null,
|
||||
updateTasks ? mViewCallbacks::updateIconInBackground : null,
|
||||
groupTask.mSplitBounds);
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.SingleTask;
|
||||
import com.android.quickstep.util.SlideInRemoteTransition;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
@@ -281,9 +282,10 @@ public class KeyboardQuickSwitchViewController {
|
||||
return -1;
|
||||
}
|
||||
RemoteTransition remoteTransition = slideInTransition;
|
||||
if (mOnDesktop
|
||||
&& mControllers.taskbarActivityContext.canUnminimizeDesktopTask(task.task1.key.id)
|
||||
) {
|
||||
boolean canUnminimizeDesktopTask = task instanceof SingleTask singleTask
|
||||
&& mControllers.taskbarActivityContext.canUnminimizeDesktopTask(
|
||||
singleTask.getTask().key.id);
|
||||
if (mOnDesktop && canUnminimizeDesktopTask) {
|
||||
// This app is being unminimized - use our own transition runner.
|
||||
remoteTransition = new RemoteTransition(
|
||||
new DesktopAppLaunchTransition(
|
||||
|
||||
@@ -49,7 +49,7 @@ import com.android.launcher3.util.OnboardingPrefs;
|
||||
import com.android.quickstep.HomeVisibilityState;
|
||||
import com.android.quickstep.RecentsAnimationCallbacks;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.SplitTask;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
|
||||
@@ -480,8 +480,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
|
||||
@Override
|
||||
public void launchSplitTasks(
|
||||
@NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) {
|
||||
mLauncher.launchSplitTasks(groupTask, remoteTransition);
|
||||
@NonNull SplitTask splitTask, @Nullable RemoteTransition remoteTransition) {
|
||||
mLauncher.launchSplitTasks(splitTask, remoteTransition);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -155,6 +155,8 @@ import com.android.quickstep.RecentsModel;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.quickstep.util.DesktopTask;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.SingleTask;
|
||||
import com.android.quickstep.util.SplitTask;
|
||||
import com.android.quickstep.views.DesktopTaskView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
@@ -1293,11 +1295,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
|
||||
mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
|
||||
|
||||
if (tag instanceof GroupTask groupTask) {
|
||||
// TODO: b/316004172, b/343289567: Handle `DesktopTask` and `SplitTask`.
|
||||
if (tag instanceof SingleTask singleTask) {
|
||||
RemoteTransition remoteTransition =
|
||||
(areDesktopTasksVisible() && canUnminimizeDesktopTask(groupTask.task1.key.id))
|
||||
(areDesktopTasksVisible() && canUnminimizeDesktopTask(
|
||||
singleTask.getTask().key.id))
|
||||
? createDesktopAppLaunchRemoteTransition(AppLaunchType.UNMINIMIZE,
|
||||
Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
|
||||
Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
|
||||
: null;
|
||||
if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) {
|
||||
RunnableList runnableList = recents.launchRunningDesktopTaskView();
|
||||
@@ -1305,12 +1309,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
// launch.
|
||||
if (runnableList != null) {
|
||||
runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
|
||||
() -> handleGroupTaskLaunch(groupTask, remoteTransition,
|
||||
() -> handleGroupTaskLaunch(singleTask, remoteTransition,
|
||||
areDesktopTasksVisible(),
|
||||
DesktopTaskToFrontReason.TASKBAR_TAP)));
|
||||
}
|
||||
} else {
|
||||
handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible(),
|
||||
handleGroupTaskLaunch(singleTask, remoteTransition, areDesktopTasksVisible(),
|
||||
DesktopTaskToFrontReason.TASKBAR_TAP);
|
||||
}
|
||||
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
|
||||
@@ -1480,13 +1484,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
remoteTransition));
|
||||
return;
|
||||
}
|
||||
if (onDesktop) {
|
||||
boolean useRemoteTransition = canUnminimizeDesktopTask(task.task1.key.id);
|
||||
if (onDesktop && task instanceof SingleTask singleTask) {
|
||||
boolean useRemoteTransition = canUnminimizeDesktopTask(singleTask.getTask().key.id);
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
if (onStartCallback != null) {
|
||||
onStartCallback.run();
|
||||
}
|
||||
SystemUiProxy.INSTANCE.get(this).showDesktopApp(task.task1.key.id,
|
||||
SystemUiProxy.INSTANCE.get(this).showDesktopApp(singleTask.getTask().key.id,
|
||||
useRemoteTransition ? remoteTransition : null, toFrontReason);
|
||||
if (onFinishCallback != null) {
|
||||
onFinishCallback.run();
|
||||
@@ -1494,18 +1498,19 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (task.task2 == null) {
|
||||
if (task instanceof SingleTask singleTask) {
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
ActivityOptions activityOptions =
|
||||
makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED).options;
|
||||
activityOptions.setRemoteTransition(remoteTransition);
|
||||
|
||||
ActivityManagerWrapper.getInstance().startActivityFromRecents(
|
||||
task.task1.key, activityOptions);
|
||||
singleTask.getTask().key, activityOptions);
|
||||
});
|
||||
return;
|
||||
}
|
||||
mControllers.uiController.launchSplitTasks(task, remoteTransition);
|
||||
assert task instanceof SplitTask;
|
||||
mControllers.uiController.launchSplitTasks((SplitTask) task, remoteTransition);
|
||||
}
|
||||
|
||||
/** Returns whether the given task is minimized and can be unminimized. */
|
||||
|
||||
@@ -80,9 +80,9 @@ import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.views.BubbleTextHolder;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.LogUtils;
|
||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||
import com.android.quickstep.util.SingleTask;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.wm.shell.shared.draganddrop.DragAndDropConstants;
|
||||
|
||||
@@ -433,8 +433,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
null, item.user));
|
||||
}
|
||||
intent.putExtra(Intent.EXTRA_USER, item.user);
|
||||
} else if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) {
|
||||
Task task = groupTask.task1;
|
||||
} else if (tag instanceof SingleTask singleTask) {
|
||||
Task task = singleTask.getTask();
|
||||
clipDescription = new ClipDescription(task.titleDescription,
|
||||
new String[] {
|
||||
ClipDescription.MIMETYPE_APPLICATION_TASK
|
||||
|
||||
@@ -76,12 +76,14 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re
|
||||
var shownTasks: List<GroupTask> = emptyList()
|
||||
private set
|
||||
|
||||
val shownTaskIds: List<Int>
|
||||
get() = shownTasks.flatMap { shownTask -> shownTask.tasks }.map { it.key.id }
|
||||
|
||||
/**
|
||||
* The task-state of an app, i.e. whether the app has a task and what state
|
||||
* that task is in.
|
||||
* The task-state of an app, i.e. whether the app has a task and what state that task is in.
|
||||
*
|
||||
* @property taskId The ID of the task if one exists (i.e. if the state is
|
||||
* RUNNING or MINIMIZED), null otherwise (NOT_RUNNING).
|
||||
* @property taskId The ID of the task if one exists (i.e. if the state is RUNNING or
|
||||
* MINIMIZED), null otherwise (NOT_RUNNING).
|
||||
*/
|
||||
data class TaskState(val runningAppState: RunningAppState, val taskId: Int? = null)
|
||||
|
||||
@@ -214,9 +216,9 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re
|
||||
return shownHotseatItems.toTypedArray()
|
||||
}
|
||||
|
||||
private fun getOrderedAndWrappedDesktopTasks(): List<GroupTask> {
|
||||
private fun getOrderedAndWrappedDesktopTasks(): List<SingleTask> {
|
||||
val tasks = desktopTask?.tasks ?: emptyList()
|
||||
// Kind of hacky, we wrap each single task in the Desktop as a GroupTask.
|
||||
// We wrap each task in the Desktop as a `SingleTask`.
|
||||
val orderFromId = orderedRunningTaskIds.withIndex().associate { (index, id) -> id to index }
|
||||
val sortedTasks = tasks.sortedWith(compareBy(nullsLast()) { orderFromId[it.key.id] })
|
||||
return sortedTasks.map { SingleTask(it) }
|
||||
@@ -286,7 +288,7 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re
|
||||
}
|
||||
|
||||
private fun updateOrderedRunningTaskIds(): MutableList<Int> {
|
||||
val desktopTasksAsList = getOrderedAndWrappedDesktopTasks().flatMap { it.tasks }
|
||||
val desktopTasksAsList = getOrderedAndWrappedDesktopTasks().map { it.task }
|
||||
val desktopTaskIds = desktopTasksAsList.map { it.key.id }
|
||||
var newOrder =
|
||||
orderedRunningTaskIds
|
||||
@@ -311,42 +313,43 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re
|
||||
val newShownTasks =
|
||||
if (Flags.enableMultiInstanceMenuTaskbar()) {
|
||||
val deduplicatedDesktopTasks =
|
||||
desktopTasks.distinctBy { Pair(it.task1.key.packageName, it.task1.key.userId) }
|
||||
desktopTasks.distinctBy { Pair(it.task.key.packageName, it.task.key.userId) }
|
||||
|
||||
shownTasks
|
||||
.filter {
|
||||
!it.supportsMultipleTasks() &&
|
||||
it.task1.key.id in deduplicatedDesktopTasks.map { it.task1.key.id }
|
||||
it is SingleTask &&
|
||||
it.task.key.id in deduplicatedDesktopTasks.map { it.task.key.id }
|
||||
}
|
||||
.toMutableList()
|
||||
.apply {
|
||||
addAll(
|
||||
deduplicatedDesktopTasks.filter { currentTask ->
|
||||
val currentTaskKey = currentTask.task1.key
|
||||
currentTaskKey.id !in shownTasks.map { it.task1.key.id } &&
|
||||
val currentTaskKey = currentTask.task.key
|
||||
currentTaskKey.id !in shownTaskIds &&
|
||||
shownHotseatItems.none { hotseatItem ->
|
||||
hotseatItem.targetPackage == currentTaskKey.packageName &&
|
||||
hotseatItem.user.identifier == currentTaskKey.userId
|
||||
currentTask.containsPackage(
|
||||
hotseatItem.targetPackage,
|
||||
hotseatItem.user.identifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val desktopTaskIds = desktopTasks.map { it.task1.key.id }
|
||||
val desktopTaskIds = desktopTasks.map { it.task.key.id }
|
||||
val shownHotseatItemTaskIds =
|
||||
shownHotseatItems.mapNotNull { it as? TaskItemInfo }.map { it.taskId }
|
||||
|
||||
shownTasks
|
||||
.filter { !it.supportsMultipleTasks() && it.task1.key.id in desktopTaskIds }
|
||||
.filter { it is SingleTask && it.task.key.id in desktopTaskIds }
|
||||
.toMutableList()
|
||||
.apply {
|
||||
addAll(
|
||||
desktopTasks.filter { desktopTask ->
|
||||
desktopTask.task1.key.id !in
|
||||
shownTasks.map { shownTask -> shownTask.task1.key.id }
|
||||
desktopTask.task.key.id !in shownTaskIds
|
||||
}
|
||||
)
|
||||
removeAll { it.task1.key.id in shownHotseatItemTaskIds }
|
||||
removeAll { it is SingleTask && it.task.key.id in shownHotseatItemTaskIds }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,21 +374,28 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re
|
||||
groupTasks: List<GroupTask>,
|
||||
shownHotseatItems: List<ItemInfo>,
|
||||
): List<GroupTask> {
|
||||
// TODO: b/393476333 - Check the behavior of the Taskbar recents section when empty desks
|
||||
// become supported.
|
||||
return if (Flags.enableMultiInstanceMenuTaskbar()) {
|
||||
groupTasks.filter { groupTask ->
|
||||
val taskKey = groupTask.task1.key
|
||||
// Keep tasks that are group tasks or unique package name/user combinations
|
||||
groupTask.hasMultipleTasks() ||
|
||||
shownHotseatItems.none {
|
||||
it.targetPackage == taskKey.packageName &&
|
||||
it.user.identifier == taskKey.userId
|
||||
}
|
||||
when (groupTask) {
|
||||
is SingleTask ->
|
||||
shownHotseatItems.none {
|
||||
groupTask.containsPackage(it.targetPackage, it.user.identifier)
|
||||
}
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val hotseatPackages = shownHotseatItems.map { it.targetPackage }
|
||||
groupTasks.filter { groupTask ->
|
||||
groupTask.hasMultipleTasks() ||
|
||||
!hotseatPackages.contains(groupTask.task1.key.packageName)
|
||||
when (groupTask) {
|
||||
is SingleTask -> hotseatPackages.none { groupTask.containsPackage(it) }
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.SplitConfigurationOptions;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.SplitTask;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskContainer;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
@@ -332,7 +332,7 @@ public class TaskbarUIController implements BubbleBarController.BubbleBarLocatio
|
||||
* Launches the given task in split-screen.
|
||||
*/
|
||||
public void launchSplitTasks(
|
||||
@NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) { }
|
||||
@NonNull SplitTask splitTask, @Nullable RemoteTransition remoteTransition) { }
|
||||
|
||||
/**
|
||||
* Returns the matching view (if any) in the taskbar.
|
||||
|
||||
@@ -70,6 +70,7 @@ import com.android.launcher3.util.LauncherBindableItemsContainer;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.SingleTask;
|
||||
import com.android.quickstep.views.TaskViewType;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
|
||||
@@ -657,9 +658,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
final Set<GroupTask> recentTasksSet = new ArraySet<>(recentTasks);
|
||||
for (GroupTask task : recentTasks) {
|
||||
if (mTaskbarOverflowView != null && overflownTasks != null
|
||||
&& overflownTasks.size() < itemsToAddToOverflow) {
|
||||
&& overflownTasks.size() < itemsToAddToOverflow
|
||||
&& task instanceof SingleTask singleTask) {
|
||||
// TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
|
||||
overflownTasks.add(task.task1);
|
||||
overflownTasks.add(singleTask.getTask());
|
||||
if (overflownTasks.size() == itemsToAddToOverflow) {
|
||||
mTaskbarOverflowView.setItems(overflownTasks);
|
||||
}
|
||||
@@ -733,18 +735,22 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
&& tagClass.isInstance(getChildAt(mNextViewIndex).getTag());
|
||||
}
|
||||
|
||||
/** Binds the GroupTask to the BubbleTextView to be ready to present to the user. */
|
||||
/** Binds the SingleTask to the BubbleTextView to be ready to present to the user. */
|
||||
public void applyGroupTaskToBubbleTextView(BubbleTextView btv, GroupTask groupTask) {
|
||||
// TODO(b/343289567): support app pairs.
|
||||
Task task1 = groupTask.task1;
|
||||
if (!(groupTask instanceof SingleTask singleTask)) {
|
||||
// TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
|
||||
return;
|
||||
}
|
||||
|
||||
Task task = singleTask.getTask();
|
||||
// TODO(b/344038728): use FastBitmapDrawable instead of Drawable, to get disabled state
|
||||
// while dragging.
|
||||
Drawable taskIcon = groupTask.task1.icon;
|
||||
Drawable taskIcon = task.icon;
|
||||
if (taskIcon != null) {
|
||||
taskIcon = taskIcon.getConstantState().newDrawable().mutate();
|
||||
}
|
||||
btv.applyIconAndLabel(taskIcon, task1.titleDescription);
|
||||
btv.setTag(groupTask);
|
||||
btv.applyIconAndLabel(taskIcon, task.titleDescription);
|
||||
btv.setTag(singleTask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,6 +92,7 @@ import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
|
||||
import com.android.launcher3.util.MultiTranslateDelegate;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.SingleTask;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
|
||||
|
||||
@@ -739,9 +740,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
|
||||
return mControllers.taskbarRecentAppsController.getRunningAppState(
|
||||
itemInfo.getTaskId());
|
||||
}
|
||||
if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) {
|
||||
if (tag instanceof SingleTask singleTask) {
|
||||
return mControllers.taskbarRecentAppsController.getRunningAppState(
|
||||
groupTask.task1.key.id);
|
||||
singleTask.getTask().key.id);
|
||||
}
|
||||
return BubbleTextView.RunningAppState.NOT_RUNNING;
|
||||
}
|
||||
|
||||
@@ -178,10 +178,10 @@ import com.android.quickstep.TaskUtils;
|
||||
import com.android.quickstep.TouchInteractionService.TISBinder;
|
||||
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
|
||||
import com.android.quickstep.util.AsyncClockEventDelegate;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.LauncherUnfoldAnimationController;
|
||||
import com.android.quickstep.util.QuickstepOnboardingPrefs;
|
||||
import com.android.quickstep.util.SplitSelectStateController;
|
||||
import com.android.quickstep.util.SplitTask;
|
||||
import com.android.quickstep.util.SplitToWorkspaceController;
|
||||
import com.android.quickstep.util.SplitWithKeyboardShortcutController;
|
||||
import com.android.quickstep.util.TISBindHelper;
|
||||
@@ -1386,33 +1386,23 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the given {@link GroupTask} in splitscreen.
|
||||
* Launches the given {@link SplitTask} in splitscreen.
|
||||
*/
|
||||
public void launchSplitTasks(
|
||||
@NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) {
|
||||
// SplitBounds can be null if coming from Taskbar launch.
|
||||
final boolean firstTaskIsLeftTopTask = isFirstTaskLeftTopTask(groupTask);
|
||||
// task2 should never be null when calling this method. Allow a crash to catch invalid calls
|
||||
Task task1 = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
|
||||
Task task2 = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
|
||||
@NonNull SplitTask splitTask, @Nullable RemoteTransition remoteTransition) {
|
||||
mSplitSelectStateController.launchExistingSplitPair(
|
||||
null /* launchingTaskView */,
|
||||
task1.key.id,
|
||||
task2.key.id,
|
||||
splitTask.getTopLeftTask().key.id,
|
||||
splitTask.getBottomRightTask().key.id,
|
||||
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
|
||||
/* callback= */ success -> mSplitSelectStateController.resetState(),
|
||||
/* freezeTaskList= */ false,
|
||||
groupTask.mSplitBounds == null
|
||||
splitTask.mSplitBounds == null
|
||||
? SNAP_TO_2_50_50
|
||||
: groupTask.mSplitBounds.snapPosition,
|
||||
: splitTask.mSplitBounds.snapPosition,
|
||||
remoteTransition);
|
||||
}
|
||||
|
||||
private static boolean isFirstTaskLeftTopTask(@NonNull GroupTask groupTask) {
|
||||
return groupTask.mSplitBounds == null
|
||||
|| groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches two apps as an app pair.
|
||||
*/
|
||||
|
||||
@@ -27,8 +27,6 @@ class DesktopTask(override val tasks: List<Task>) :
|
||||
|
||||
override fun containsTask(taskId: Int) = tasks.any { it.key.id == taskId }
|
||||
|
||||
override fun hasMultipleTasks() = tasks.size > 1
|
||||
|
||||
override fun supportsMultipleTasks() = true
|
||||
|
||||
override fun copy() = DesktopTask(tasks)
|
||||
|
||||
@@ -50,9 +50,16 @@ constructor(
|
||||
/**
|
||||
* Returns true if a task in this group has a package name that matches the given `packageName`.
|
||||
*/
|
||||
fun containsPackage(packageName: String) = tasks.any { it.key.packageName == packageName }
|
||||
fun containsPackage(packageName: String?) = tasks.any { it.key.packageName == packageName }
|
||||
|
||||
open fun hasMultipleTasks() = task2 != null
|
||||
/**
|
||||
* Returns true if a task in this group has a package name that matches the given `packageName`,
|
||||
* and its user ID matches the given `userId`.
|
||||
*/
|
||||
fun containsPackage(packageName: String?, userId: Int) =
|
||||
tasks.any { it.key.packageName == packageName && it.key.userId == userId }
|
||||
|
||||
fun isEmpty() = tasks.isEmpty()
|
||||
|
||||
/** Returns whether this task supports multiple tasks or not. */
|
||||
open fun supportsMultipleTasks() = taskViewType == TaskViewType.GROUPED
|
||||
@@ -78,6 +85,10 @@ constructor(
|
||||
/** A [Task] container that must contain exactly one task in the recent tasks list. */
|
||||
class SingleTask(task: Task) :
|
||||
GroupTask(task, task2 = null, mSplitBounds = null, TaskViewType.SINGLE) {
|
||||
|
||||
val task: Task
|
||||
get() = task1
|
||||
|
||||
override fun copy() = SingleTask(task1)
|
||||
|
||||
override fun toString() = "type=$taskViewType task=$task1"
|
||||
@@ -96,6 +107,12 @@ class SingleTask(task: Task) :
|
||||
class SplitTask(task1: Task, task2: Task, splitBounds: SplitConfigurationOptions.SplitBounds) :
|
||||
GroupTask(task1, task2, splitBounds, TaskViewType.GROUPED) {
|
||||
|
||||
val topLeftTask: Task
|
||||
get() = if (mSplitBounds!!.leftTopTaskId == task1.key.id) task1!! else task2!!
|
||||
|
||||
val bottomRightTask: Task
|
||||
get() = if (topLeftTask == task1) task2!! else task1!!
|
||||
|
||||
override fun copy() = SplitTask(task1, task2!!, mSplitBounds!!)
|
||||
|
||||
override fun toString() = "type=$taskViewType task1=$task1 task2=$task2"
|
||||
|
||||
@@ -71,7 +71,6 @@ import com.android.internal.logging.InstanceId;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.apppairs.AppPairIcon;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
@@ -260,7 +259,7 @@ public class SplitSelectStateController {
|
||||
GroupTask groupTask = taskGroups.get(i);
|
||||
if (isInstanceOfAppPair(
|
||||
groupTask, componentKeys.get(0), componentKeys.get(1))) {
|
||||
lastActiveTasks[0] = groupTask.task1;
|
||||
lastActiveTasks[0] = ((SplitTask) groupTask).getTopLeftTask();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -314,11 +313,15 @@ public class SplitSelectStateController {
|
||||
*/
|
||||
public boolean isInstanceOfAppPair(GroupTask groupTask, @NonNull ComponentKey componentKey1,
|
||||
@NonNull ComponentKey componentKey2) {
|
||||
return ((isInstanceOfComponent(groupTask.task1, componentKey1)
|
||||
&& isInstanceOfComponent(groupTask.task2, componentKey2))
|
||||
||
|
||||
(isInstanceOfComponent(groupTask.task1, componentKey2)
|
||||
&& isInstanceOfComponent(groupTask.task2, componentKey1)));
|
||||
if (groupTask instanceof SplitTask splitTask) {
|
||||
return ((isInstanceOfComponent(splitTask.getTopLeftTask(), componentKey1)
|
||||
&& isInstanceOfComponent(splitTask.getBottomRightTask(), componentKey2))
|
||||
||
|
||||
(isInstanceOfComponent(splitTask.getTopLeftTask(), componentKey2)
|
||||
&& isInstanceOfComponent(splitTask.getBottomRightTask(),
|
||||
componentKey1)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -212,9 +212,11 @@ import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
|
||||
import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.SingleTask;
|
||||
import com.android.quickstep.util.SplitAnimationController.Companion.SplitAnimInitProps;
|
||||
import com.android.quickstep.util.SplitAnimationTimings;
|
||||
import com.android.quickstep.util.SplitSelectStateController;
|
||||
import com.android.quickstep.util.SplitTask;
|
||||
import com.android.quickstep.util.SurfaceTransaction;
|
||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||
import com.android.quickstep.util.TaskGridNavHelper;
|
||||
@@ -1968,7 +1970,7 @@ public abstract class RecentsView<
|
||||
GroupTask groupTask = taskGroups.get(i);
|
||||
boolean containsStagedTask = stagedTaskIdToBeRemoved != INVALID_TASK_ID
|
||||
&& groupTask.containsTask(stagedTaskIdToBeRemoved);
|
||||
boolean shouldSkipGroupTask = containsStagedTask && !groupTask.hasMultipleTasks();
|
||||
boolean shouldSkipGroupTask = containsStagedTask && groupTask instanceof SingleTask;
|
||||
|
||||
if ((isSplitSelectionActive() && groupTask.taskViewType == TaskViewType.DESKTOP)
|
||||
|| shouldSkipGroupTask) {
|
||||
@@ -1982,25 +1984,27 @@ public abstract class RecentsView<
|
||||
// to be a temporary container for the remaining task.
|
||||
TaskView taskView = getTaskViewFromPool(
|
||||
containsStagedTask ? TaskViewType.SINGLE : groupTask.taskViewType);
|
||||
if (taskView instanceof GroupedTaskView) {
|
||||
boolean firstTaskIsLeftTopTask =
|
||||
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
|
||||
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
|
||||
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
|
||||
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
|
||||
mTaskOverlayFactory, groupTask.mSplitBounds);
|
||||
} else if (taskView instanceof DesktopTaskView) {
|
||||
if (taskView instanceof GroupedTaskView groupedTaskView) {
|
||||
var splitTask = (SplitTask) groupTask;
|
||||
groupedTaskView.bind(splitTask.getTopLeftTask(),
|
||||
splitTask.getBottomRightTask(), mOrientationState,
|
||||
mTaskOverlayFactory, splitTask.mSplitBounds);
|
||||
} else if (taskView instanceof DesktopTaskView desktopTaskView) {
|
||||
// Minimized tasks should not be shown in Overview
|
||||
List<Task> nonMinimizedTasks =
|
||||
groupTask.getTasks().stream()
|
||||
.filter(task -> !task.isMinimized)
|
||||
.toList();
|
||||
((DesktopTaskView) taskView).bind(nonMinimizedTasks, mOrientationState,
|
||||
desktopTaskView.bind(nonMinimizedTasks, mOrientationState,
|
||||
mTaskOverlayFactory);
|
||||
} else {
|
||||
Task task = groupTask.task1.key.id == stagedTaskIdToBeRemoved ? groupTask.task2
|
||||
: groupTask.task1;
|
||||
} else if (groupTask instanceof SplitTask splitTask) {
|
||||
Task task = splitTask.getTopLeftTask().key.id == stagedTaskIdToBeRemoved
|
||||
? splitTask.getBottomRightTask()
|
||||
: splitTask.getTopLeftTask();
|
||||
taskView.bind(task, mOrientationState, mTaskOverlayFactory);
|
||||
} else {
|
||||
taskView.bind(((SingleTask) groupTask).getTask(), mOrientationState,
|
||||
mTaskOverlayFactory);
|
||||
}
|
||||
addView(taskView);
|
||||
|
||||
|
||||
+37
-37
@@ -102,12 +102,12 @@ class SplitSelectStateControllerTest {
|
||||
fun activeTasks_noMatchingTasks() {
|
||||
val nonMatchingComponent = ComponentKey(ComponentName("no", "match"), primaryUserHandle)
|
||||
val groupTask1 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pomegranate", "juice"),
|
||||
ComponentName("pumpkin", "pie"),
|
||||
)
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("hotdog", "juice"),
|
||||
ComponentName("personal", "computer"),
|
||||
)
|
||||
@@ -143,12 +143,12 @@ class SplitSelectStateControllerTest {
|
||||
val matchingComponent =
|
||||
ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
|
||||
val groupTask1 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
ComponentName("pomegranate", "juice"),
|
||||
)
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pumpkin", "pie"),
|
||||
ComponentName("personal", "computer"),
|
||||
)
|
||||
@@ -170,7 +170,7 @@ class SplitSelectStateControllerTest {
|
||||
it[0].key.baseIntent.component?.className,
|
||||
matchingClass,
|
||||
)
|
||||
assertEquals(it[0], groupTask1.task1)
|
||||
assertEquals(it[0], groupTask1.topLeftTask)
|
||||
}
|
||||
|
||||
// Capture callback from recentsModel#getTasks()
|
||||
@@ -196,12 +196,12 @@ class SplitSelectStateControllerTest {
|
||||
val nonPrimaryUserComponent =
|
||||
ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle)
|
||||
val groupTask1 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
ComponentName("pomegranate", "juice"),
|
||||
)
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pumpkin", "pie"),
|
||||
ComponentName("personal", "computer"),
|
||||
)
|
||||
@@ -237,14 +237,14 @@ class SplitSelectStateControllerTest {
|
||||
val nonPrimaryUserComponent =
|
||||
ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle)
|
||||
val groupTask1 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
nonPrimaryUserHandle,
|
||||
ComponentName("pomegranate", "juice"),
|
||||
nonPrimaryUserHandle,
|
||||
)
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pumpkin", "pie"),
|
||||
ComponentName("personal", "computer"),
|
||||
)
|
||||
@@ -267,7 +267,7 @@ class SplitSelectStateControllerTest {
|
||||
matchingClass,
|
||||
)
|
||||
assertEquals("userId mismatched", it[0].key.userId, nonPrimaryUserHandle.identifier)
|
||||
assertEquals(it[0], groupTask1.task1)
|
||||
assertEquals(it[0], groupTask1.topLeftTask)
|
||||
}
|
||||
|
||||
// Capture callback from recentsModel#getTasks()
|
||||
@@ -293,12 +293,12 @@ class SplitSelectStateControllerTest {
|
||||
val matchingComponent =
|
||||
ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
|
||||
val groupTask1 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
ComponentName("pumpkin", "pie"),
|
||||
)
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pomegranate", "juice"),
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
)
|
||||
@@ -320,7 +320,7 @@ class SplitSelectStateControllerTest {
|
||||
it[0].key.baseIntent.component?.className,
|
||||
matchingClass,
|
||||
)
|
||||
assertEquals(it[0], groupTask1.task1)
|
||||
assertEquals(it[0], groupTask1.topLeftTask)
|
||||
}
|
||||
|
||||
// Capture callback from recentsModel#getTasks()
|
||||
@@ -348,9 +348,9 @@ class SplitSelectStateControllerTest {
|
||||
ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
|
||||
|
||||
val groupTask1 =
|
||||
generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
|
||||
generateSplitTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pomegranate", "juice"),
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
)
|
||||
@@ -374,7 +374,7 @@ class SplitSelectStateControllerTest {
|
||||
it[1].key.baseIntent.component?.className,
|
||||
matchingClass,
|
||||
)
|
||||
assertEquals(it[1], groupTask2.task2)
|
||||
assertEquals(it[1], groupTask2.bottomRightTask)
|
||||
}
|
||||
|
||||
// Capture callback from recentsModel#getTasks()
|
||||
@@ -401,9 +401,9 @@ class SplitSelectStateControllerTest {
|
||||
ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
|
||||
|
||||
val groupTask1 =
|
||||
generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
|
||||
generateSplitTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pomegranate", "juice"),
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
)
|
||||
@@ -426,7 +426,7 @@ class SplitSelectStateControllerTest {
|
||||
it[0].key.baseIntent.component?.className,
|
||||
matchingClass,
|
||||
)
|
||||
assertEquals(it[0], groupTask2.task2)
|
||||
assertEquals(it[0], groupTask2.bottomRightTask)
|
||||
assertNull("No tasks should have matched", it[1] /*task*/)
|
||||
}
|
||||
|
||||
@@ -454,12 +454,12 @@ class SplitSelectStateControllerTest {
|
||||
ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
|
||||
|
||||
val groupTask1 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
ComponentName("pumpkin", "pie"),
|
||||
)
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("pomegranate", "juice"),
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
)
|
||||
@@ -482,7 +482,7 @@ class SplitSelectStateControllerTest {
|
||||
it[0].key.baseIntent.component?.className,
|
||||
matchingClass,
|
||||
)
|
||||
assertEquals(it[0], groupTask1.task1)
|
||||
assertEquals(it[0], groupTask1.topLeftTask)
|
||||
assertEquals(
|
||||
"ComponentName package mismatched",
|
||||
it[1].key.baseIntent.component?.packageName,
|
||||
@@ -493,7 +493,7 @@ class SplitSelectStateControllerTest {
|
||||
it[1].key.baseIntent.component?.className,
|
||||
matchingClass,
|
||||
)
|
||||
assertEquals(it[1], groupTask2.task2)
|
||||
assertEquals(it[1], groupTask2.bottomRightTask)
|
||||
}
|
||||
|
||||
// Capture callback from recentsModel#getTasks()
|
||||
@@ -524,14 +524,14 @@ class SplitSelectStateControllerTest {
|
||||
ComponentKey(ComponentName(matchingPackage2, matchingClass2), primaryUserHandle)
|
||||
|
||||
val groupTask1 =
|
||||
generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
|
||||
generateSplitTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
|
||||
val groupTask2 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName(matchingPackage2, matchingClass2),
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
)
|
||||
val groupTask3 =
|
||||
generateGroupTask(
|
||||
generateSplitTask(
|
||||
ComponentName("hotdog", "pie"),
|
||||
ComponentName(matchingPackage, matchingClass),
|
||||
)
|
||||
@@ -545,7 +545,7 @@ class SplitSelectStateControllerTest {
|
||||
val taskConsumer =
|
||||
Consumer<Array<Task>> {
|
||||
assertEquals("Expected array length 2", 2, it.size)
|
||||
assertEquals("Found wrong task", it[0], groupTask2.task1)
|
||||
assertEquals("Found wrong task", it[0], groupTask2.topLeftTask)
|
||||
}
|
||||
|
||||
// Capture callback from recentsModel#getTasks()
|
||||
@@ -640,11 +640,11 @@ class SplitSelectStateControllerTest {
|
||||
verify(recentsView, times(0)).resetDesktopTaskFromSplitSelectState()
|
||||
}
|
||||
|
||||
// Generate GroupTask with default userId.
|
||||
private fun generateGroupTask(
|
||||
/** Generates a [SplitTask] with default userId. */
|
||||
private fun generateSplitTask(
|
||||
task1ComponentName: ComponentName,
|
||||
task2ComponentName: ComponentName,
|
||||
): GroupTask {
|
||||
): SplitTask {
|
||||
val task1 = Task()
|
||||
var taskInfo = ActivityManager.RunningTaskInfo()
|
||||
taskInfo.taskId = getUniqueId()
|
||||
@@ -666,20 +666,20 @@ class SplitSelectStateControllerTest {
|
||||
SplitConfigurationOptions.SplitBounds(
|
||||
/* leftTopBounds = */ Rect(),
|
||||
/* rightBottomBounds = */ Rect(),
|
||||
/* leftTopTaskId = */ -1,
|
||||
/* rightBottomTaskId = */ -1,
|
||||
/* leftTopTaskId = */ task1.key.id,
|
||||
/* rightBottomTaskId = */ task2.key.id,
|
||||
/* snapPosition = */ SNAP_TO_2_50_50,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Generate GroupTask with custom user handles.
|
||||
private fun generateGroupTask(
|
||||
/** Generates a [SplitTask] with custom user handles. */
|
||||
private fun generateSplitTask(
|
||||
task1ComponentName: ComponentName,
|
||||
userHandle1: UserHandle,
|
||||
task2ComponentName: ComponentName,
|
||||
userHandle2: UserHandle,
|
||||
): GroupTask {
|
||||
): SplitTask {
|
||||
val task1 = Task()
|
||||
var taskInfo = ActivityManager.RunningTaskInfo()
|
||||
taskInfo.taskId = getUniqueId()
|
||||
@@ -704,8 +704,8 @@ class SplitSelectStateControllerTest {
|
||||
SplitConfigurationOptions.SplitBounds(
|
||||
/* leftTopBounds = */ Rect(),
|
||||
/* rightBottomBounds = */ Rect(),
|
||||
/* leftTopTaskId = */ -1,
|
||||
/* rightBottomTaskId = */ -1,
|
||||
/* leftTopTaskId = */ task1.key.id,
|
||||
/* rightBottomTaskId = */ task2.key.id,
|
||||
/* snapPosition = */ SNAP_TO_2_50_50,
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user