Allow children task of created-by-organizer task to be organized
Allows to organize direct child of root task created by organizer and extends TaskInfo to reveal the parent task id. To organize child task properly for each features, passes all child task organizer events to the listener listening to its parent task if feasible. Fix: 169267298 Fix: 169266482 Test: atest WindowOrganizerTests Test: atest SplitScreenTests Test: atest PinnedStackTests Test: atest WindowInsetPolicyTests Test: atest WMShellUnitTests Test: split screen works Change-Id: I8e66f810c86fcdac3287adfa208d8e34786fc9b5
This commit is contained in:
@@ -286,6 +286,7 @@ package android.app {
|
||||
public class TaskInfo {
|
||||
method @NonNull public android.content.res.Configuration getConfiguration();
|
||||
method @NonNull public android.window.WindowContainerToken getToken();
|
||||
method public boolean hasParentTask();
|
||||
}
|
||||
|
||||
public class TimePickerDialog extends android.app.AlertDialog implements android.content.DialogInterface.OnClickListener android.widget.TimePicker.OnTimeChangedListener {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
@@ -203,6 +205,13 @@ public class TaskInfo {
|
||||
*/
|
||||
public ArrayList<IBinder> launchCookies = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The identifier of the parent task that is created by organizer, otherwise
|
||||
* {@link ActivityTaskManager#INVALID_TASK_ID}.
|
||||
* @hide
|
||||
*/
|
||||
public int parentTaskId;
|
||||
|
||||
TaskInfo() {
|
||||
// Do nothing
|
||||
}
|
||||
@@ -245,6 +254,12 @@ public class TaskInfo {
|
||||
launchCookies.add(cookie);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@TestApi
|
||||
public boolean hasParentTask() {
|
||||
return parentTaskId != INVALID_TASK_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the TaskInfo from a parcel.
|
||||
*/
|
||||
@@ -275,6 +290,7 @@ public class TaskInfo {
|
||||
source.readBinderList(launchCookies);
|
||||
letterboxActivityBounds = source.readTypedObject(Rect.CREATOR);
|
||||
positionInParent = source.readTypedObject(Point.CREATOR);
|
||||
parentTaskId = source.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,6 +324,7 @@ public class TaskInfo {
|
||||
dest.writeBinderList(launchCookies);
|
||||
dest.writeTypedObject(letterboxActivityBounds, flags);
|
||||
dest.writeTypedObject(positionInParent, flags);
|
||||
dest.writeInt(parentTaskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -330,6 +347,7 @@ public class TaskInfo {
|
||||
+ " launchCookies" + launchCookies
|
||||
+ " letterboxActivityBounds=" + letterboxActivityBounds
|
||||
+ " positionInParent=" + positionInParent
|
||||
+ " parentTaskId: " + parentTaskId
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,6 +335,12 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
listener = mTaskListeners.get(taskId);
|
||||
if (listener != null) return listener;
|
||||
|
||||
// Next priority goes to the listener listening to its parent.
|
||||
if (runningTaskInfo.hasParentTask()) {
|
||||
listener = mTaskListeners.get(runningTaskInfo.parentTaskId);
|
||||
if (listener != null) return listener;
|
||||
}
|
||||
|
||||
// Next we try type specific listeners.
|
||||
final int taskListenerType = taskInfoToTaskListenerType(runningTaskInfo);
|
||||
return mTaskListeners.get(taskListenerType);
|
||||
|
||||
@@ -117,7 +117,7 @@ public class SplitScreenController implements SplitScreen,
|
||||
mTransactionPool = transactionPool;
|
||||
mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer);
|
||||
mTaskOrganizer = shellTaskOrganizer;
|
||||
mSplits = new SplitScreenTaskListener(this, shellTaskOrganizer);
|
||||
mSplits = new SplitScreenTaskListener(this, shellTaskOrganizer, syncQueue);
|
||||
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler,
|
||||
shellTaskOrganizer);
|
||||
mRotationController =
|
||||
|
||||
@@ -27,8 +27,10 @@ import static com.android.wm.shell.ShellTaskOrganizer.getWindowingMode;
|
||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
|
||||
@@ -36,6 +38,8 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.Transitions;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -44,6 +48,8 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
private static final boolean DEBUG = SplitScreenController.DEBUG;
|
||||
|
||||
private final ShellTaskOrganizer mTaskOrganizer;
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
|
||||
|
||||
RunningTaskInfo mPrimary;
|
||||
RunningTaskInfo mSecondary;
|
||||
@@ -58,9 +64,11 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
|
||||
SplitScreenTaskListener(SplitScreenController splitScreenController,
|
||||
ShellTaskOrganizer shellTaskOrganizer) {
|
||||
ShellTaskOrganizer shellTaskOrganizer,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
mSplitScreenController = splitScreenController;
|
||||
mTaskOrganizer = shellTaskOrganizer;
|
||||
mSyncQueue = syncQueue;
|
||||
}
|
||||
|
||||
void init() {
|
||||
@@ -93,6 +101,11 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
@Override
|
||||
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
synchronized (this) {
|
||||
if (taskInfo.hasParentTask()) {
|
||||
handleChildTaskAppeared(taskInfo, leash);
|
||||
return;
|
||||
}
|
||||
|
||||
final int winMode = getWindowingMode(taskInfo);
|
||||
if (winMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
|
||||
ProtoLog.v(WM_SHELL_TASK_ORG,
|
||||
@@ -139,6 +152,11 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
@Override
|
||||
public void onTaskVanished(RunningTaskInfo taskInfo) {
|
||||
synchronized (this) {
|
||||
if (taskInfo.hasParentTask()) {
|
||||
mLeashByTaskId.remove(taskInfo.taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isPrimaryTask = mPrimary != null
|
||||
&& taskInfo.token.equals(mPrimary.token);
|
||||
final boolean isSecondaryTask = mSecondary != null
|
||||
@@ -165,7 +183,41 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
if (taskInfo.displayId != DEFAULT_DISPLAY) {
|
||||
return;
|
||||
}
|
||||
mSplitScreenController.post(() -> handleTaskInfoChanged(taskInfo));
|
||||
synchronized (this) {
|
||||
if (taskInfo.hasParentTask()) {
|
||||
handleChildTaskChanged(taskInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
mSplitScreenController.post(() -> handleTaskInfoChanged(taskInfo));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
mLeashByTaskId.put(taskInfo.taskId, leash);
|
||||
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
|
||||
}
|
||||
|
||||
private void handleChildTaskChanged(RunningTaskInfo taskInfo) {
|
||||
final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
|
||||
updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */);
|
||||
}
|
||||
|
||||
private void updateChildTaskSurface(
|
||||
RunningTaskInfo taskInfo, SurfaceControl leash, boolean firstAppeared) {
|
||||
final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
|
||||
final Point taskPositionInParent = taskInfo.positionInParent;
|
||||
final Rect corp = new Rect(taskBounds);
|
||||
corp.offset(-taskBounds.left, -taskBounds.top);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
t.setWindowCrop(leash, corp);
|
||||
t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
|
||||
if (firstAppeared && !Transitions.ENABLE_SHELL_TRANSITIONS) {
|
||||
t.setAlpha(leash, 1f);
|
||||
t.setMatrix(leash, 1, 0, 0, 1);
|
||||
t.show(leash);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -247,6 +247,20 @@ public class ShellTaskOrganizerTests {
|
||||
assertTrue(gotException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParentTaskListener() {
|
||||
RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
|
||||
TrackingTaskListener mwListener = new TrackingTaskListener();
|
||||
mOrganizer.onTaskAppeared(task1, null);
|
||||
mOrganizer.addListenerForTaskId(mwListener, task1.taskId);
|
||||
RunningTaskInfo task2 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
|
||||
task2.parentTaskId = task1.taskId;
|
||||
|
||||
mOrganizer.onTaskAppeared(task2, null);
|
||||
|
||||
assertTrue(mwListener.appeared.contains(task2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskInfoToTaskListenerType_whenLetterboxBoundsPassed_returnsLetterboxType() {
|
||||
RunningTaskInfo taskInfo = createTaskInfo(
|
||||
|
||||
@@ -125,7 +125,6 @@ import static com.android.server.wm.Task.ActivityState.PAUSED;
|
||||
import static com.android.server.wm.Task.ActivityState.PAUSING;
|
||||
import static com.android.server.wm.Task.ActivityState.RESUMED;
|
||||
import static com.android.server.wm.Task.ActivityState.STARTED;
|
||||
import static com.android.server.wm.Task.ActivityState.STOPPED;
|
||||
import static com.android.server.wm.Task.ActivityState.STOPPING;
|
||||
import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
|
||||
import static com.android.server.wm.TaskProto.BOUNDS;
|
||||
@@ -4113,6 +4112,10 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
forAllActivities(r -> {
|
||||
info.addLaunchCookie(r.mLaunchCookie);
|
||||
});
|
||||
final Task rootTask = getRootTask();
|
||||
info.parentTaskId = rootTask == getParent() && rootTask.mCreatedByOrganizer
|
||||
? rootTask.mTaskId
|
||||
: INVALID_TASK_ID;
|
||||
}
|
||||
|
||||
@Nullable PictureInPictureParams getPictureInPictureParams() {
|
||||
@@ -4829,6 +4832,17 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
return mTaskOrganizer != null;
|
||||
}
|
||||
|
||||
private boolean canBeOrganized() {
|
||||
// All root tasks can be organized
|
||||
if (isRootTask()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Task could be organized if it's the direct child of the root created by organizer.
|
||||
final Task rootTask = getRootTask();
|
||||
return rootTask == getParent() && rootTask.mCreatedByOrganizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean showSurfaceOnCreation() {
|
||||
// Organized tasks handle their own surface visibility
|
||||
@@ -4977,7 +4991,7 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
// is created.
|
||||
return false;
|
||||
}
|
||||
if (!isRootTask()) {
|
||||
if (!canBeOrganized()) {
|
||||
return setTaskOrganizer(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -976,6 +976,21 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReparentToOrganizedTask() {
|
||||
final ITaskOrganizer organizer = registerMockOrganizer();
|
||||
Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
|
||||
mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
|
||||
final Task task1 = createStack();
|
||||
final Task task2 = createTask(rootTask, false /* fakeDraw */);
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.reparent(task1.mRemoteToken.toWindowContainerToken(),
|
||||
rootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
|
||||
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
|
||||
assertTrue(task1.isOrganized());
|
||||
assertTrue(task2.isOrganized());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that task vanished is called for a specific task.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user