Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +20 −2 Original line number Diff line number Diff line Loading @@ -41,10 +41,13 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.StagedSplitBounds; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Manages the recent task list from the system, caching it as necessary. Loading @@ -62,6 +65,13 @@ public class RecentTasksController implements TaskStackListenerCallback, // Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a // pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1) private final SparseIntArray mSplitTasks = new SparseIntArray(); /** * Maps taskId to {@link StagedSplitBounds} for both taskIDs. * Meaning there will be two taskId integers mapping to the same object. * If there's any ordering to the pairing than we can probably just get away with only one * taskID mapping to it, leaving both for consistency with {@link #mSplitTasks} for now. */ private final Map<Integer, StagedSplitBounds> mTaskSplitBoundsMap = new HashMap<>(); /** * Creates {@link RecentTasksController}, returns {@code null} if the feature is not Loading Loading @@ -97,15 +107,20 @@ public class RecentTasksController implements TaskStackListenerCallback, /** * Adds a split pair. This call does not validate the taskIds, only that they are not the same. */ public void addSplitPair(int taskId1, int taskId2) { public void addSplitPair(int taskId1, int taskId2, StagedSplitBounds splitBounds) { if (taskId1 == taskId2) { return; } // Remove any previous pairs removeSplitPair(taskId1); removeSplitPair(taskId2); mTaskSplitBoundsMap.remove(taskId1); mTaskSplitBoundsMap.remove(taskId2); mSplitTasks.put(taskId1, taskId2); mSplitTasks.put(taskId2, taskId1); mTaskSplitBoundsMap.put(taskId1, splitBounds); mTaskSplitBoundsMap.put(taskId2, splitBounds); } /** Loading @@ -116,6 +131,8 @@ public class RecentTasksController implements TaskStackListenerCallback, if (pairedTaskId != INVALID_TASK_ID) { mSplitTasks.delete(taskId); mSplitTasks.delete(pairedTaskId); mTaskSplitBoundsMap.remove(taskId); mTaskSplitBoundsMap.remove(pairedTaskId); } } Loading Loading @@ -203,7 +220,8 @@ public class RecentTasksController implements TaskStackListenerCallback, if (pairedTaskId != INVALID_TASK_ID) { final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId); rawMapping.remove(pairedTaskId); recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo)); recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo, mTaskSplitBoundsMap.get(pairedTaskId))); } else { recentTasks.add(new GroupedRecentTaskInfo(taskInfo)); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +16 −1 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.StagedSplitBounds; import java.io.PrintWriter; import java.util.ArrayList; Loading Loading @@ -691,11 +692,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } mRecentTasks.ifPresent(recentTasks -> { Rect topLeftBounds = mSplitLayout.getBounds1(); Rect bottomRightBounds = mSplitLayout.getBounds2(); int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId(); int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId(); boolean sideStageTopLeft = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; int leftTopTaskId; int rightBottomTaskId; if (sideStageTopLeft) { leftTopTaskId = sideStageTopTaskId; rightBottomTaskId = mainStageTopTaskId; } else { leftTopTaskId = mainStageTopTaskId; rightBottomTaskId = sideStageTopTaskId; } StagedSplitBounds splitBounds = new StagedSplitBounds(topLeftBounds, bottomRightBounds, leftTopTaskId, rightBottomTaskId); if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) { // Update the pair for the top tasks recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId); recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds); } }); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java +13 −3 Original line number Diff line number Diff line Loading @@ -30,25 +30,34 @@ import androidx.annotation.Nullable; public class GroupedRecentTaskInfo implements Parcelable { public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1; public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2; public @Nullable StagedSplitBounds mStagedSplitBounds; public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) { this(task1, null); this(task1, null, null); } public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1, @Nullable ActivityManager.RecentTaskInfo task2) { @Nullable ActivityManager.RecentTaskInfo task2, @Nullable StagedSplitBounds stagedSplitBounds) { mTaskInfo1 = task1; mTaskInfo2 = task2; mStagedSplitBounds = stagedSplitBounds; } GroupedRecentTaskInfo(Parcel parcel) { mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR); mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR); mStagedSplitBounds = parcel.readTypedObject(StagedSplitBounds.CREATOR); } @Override public String toString() { return "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2); String taskString = "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2); if (mStagedSplitBounds != null) { taskString += ", SplitBounds: " + mStagedSplitBounds.toString(); } return taskString; } private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) { Loading @@ -67,6 +76,7 @@ public class GroupedRecentTaskInfo implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeTypedObject(mTaskInfo1, flags); parcel.writeTypedObject(mTaskInfo2, flags); parcel.writeTypedObject(mStagedSplitBounds, flags); } @Override Loading libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java 0 → 100644 +114 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.util; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; /** * Container of various information needed to display split screen * tasks/leashes/etc in Launcher */ public class StagedSplitBounds implements Parcelable { public final Rect leftTopBounds; public final Rect rightBottomBounds; /** This rect represents the actual gap between the two apps */ public final Rect visualDividerBounds; // This class is orientation-agnostic, so we compute both for later use public final float topTaskPercent; public final float leftTaskPercent; /** * If {@code true}, that means at the time of creation of this object, the * split-screened apps were vertically stacked. This is useful in scenarios like * rotation where the bounds won't change, but this variable can indicate what orientation * the bounds were originally in */ public final boolean appsStackedVertically; public final int leftTopTaskId; public final int rightBottomTaskId; public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, int rightBottomTaskId) { this.leftTopBounds = leftTopBounds; this.rightBottomBounds = rightBottomBounds; this.leftTopTaskId = leftTopTaskId; this.rightBottomTaskId = rightBottomTaskId; if (rightBottomBounds.top > leftTopBounds.top) { // vertical apps, horizontal divider this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom, leftTopBounds.right, rightBottomBounds.top); appsStackedVertically = true; } else { // horizontal apps, vertical divider this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top, rightBottomBounds.left, leftTopBounds.bottom); appsStackedVertically = false; } leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right; topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom; } public StagedSplitBounds(Parcel parcel) { leftTopBounds = parcel.readTypedObject(Rect.CREATOR); rightBottomBounds = parcel.readTypedObject(Rect.CREATOR); visualDividerBounds = parcel.readTypedObject(Rect.CREATOR); topTaskPercent = parcel.readFloat(); leftTaskPercent = parcel.readFloat(); appsStackedVertically = parcel.readBoolean(); leftTopTaskId = parcel.readInt(); rightBottomTaskId = parcel.readInt(); } @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeTypedObject(leftTopBounds, flags); parcel.writeTypedObject(rightBottomBounds, flags); parcel.writeTypedObject(visualDividerBounds, flags); parcel.writeFloat(topTaskPercent); parcel.writeFloat(leftTaskPercent); parcel.writeBoolean(appsStackedVertically); parcel.writeInt(leftTopTaskId); parcel.writeInt(rightBottomTaskId); } @Override public int describeContents() { return 0; } @Override public String toString() { return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n" + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n" + "Divider: " + visualDividerBounds + "\n" + "AppsVertical? " + appsStackedVertically; } public static final Creator<StagedSplitBounds> CREATOR = new Creator<StagedSplitBounds>() { @Override public StagedSplitBounds createFromParcel(Parcel in) { return new StagedSplitBounds(in); } @Override public StagedSplitBounds[] newArray(int size) { return new StagedSplitBounds[size]; } }; } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +25 −6 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; Loading @@ -31,10 +33,9 @@ import static org.mockito.Mockito.verify; import static java.lang.Integer.MAX_VALUE; import android.app.ActivityManager; import android.app.WindowConfiguration; import android.content.Context; import android.graphics.Rect; import android.view.SurfaceControl; import android.window.TaskAppearedInfo; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -45,6 +46,7 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.StagedSplitBounds; import org.junit.Before; import org.junit.Test; Loading Loading @@ -106,8 +108,11 @@ public class RecentTasksControllerTest extends ShellTestCase { setRawList(t1, t2, t3, t4, t5, t6); // Mark a couple pairs [t2, t4], [t3, t5] mRecentTasksController.addSplitPair(t2.taskId, t4.taskId); mRecentTasksController.addSplitPair(t3.taskId, t5.taskId); StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 4); StagedSplitBounds pair2Bounds = new StagedSplitBounds(new Rect(), new Rect(), 3, 5); mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds); mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds); ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks( MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0); Loading @@ -126,7 +131,8 @@ public class RecentTasksControllerTest extends ShellTestCase { setRawList(t1, t2, t3); // Add a pair mRecentTasksController.addSplitPair(t2.taskId, t3.taskId); StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 3); mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds); reset(mRecentTasksController); // Remove one of the tasks and ensure the pair is removed Loading Loading @@ -201,10 +207,23 @@ public class RecentTasksControllerTest extends ShellTestCase { int[] flattenedTaskIds = new int[recentTasks.size() * 2]; for (int i = 0; i < recentTasks.size(); i++) { GroupedRecentTaskInfo pair = recentTasks.get(i); flattenedTaskIds[2 * i] = pair.mTaskInfo1.taskId; int taskId1 = pair.mTaskInfo1.taskId; flattenedTaskIds[2 * i] = taskId1; flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null ? pair.mTaskInfo2.taskId : -1; if (pair.mTaskInfo2 != null) { assertNotNull(pair.mStagedSplitBounds); int leftTopTaskId = pair.mStagedSplitBounds.leftTopTaskId; int bottomRightTaskId = pair.mStagedSplitBounds.rightBottomTaskId; // Unclear if pairs are ordered by split position, most likely not. assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId); assertTrue(bottomRightTaskId == taskId1 || bottomRightTaskId == pair.mTaskInfo2.taskId); } else { assertNull(pair.mStagedSplitBounds); } } assertTrue("Expected: " + Arrays.toString(expectedTaskIds) + " Received: " + Arrays.toString(flattenedTaskIds), Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +20 −2 Original line number Diff line number Diff line Loading @@ -41,10 +41,13 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.StagedSplitBounds; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Manages the recent task list from the system, caching it as necessary. Loading @@ -62,6 +65,13 @@ public class RecentTasksController implements TaskStackListenerCallback, // Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a // pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1) private final SparseIntArray mSplitTasks = new SparseIntArray(); /** * Maps taskId to {@link StagedSplitBounds} for both taskIDs. * Meaning there will be two taskId integers mapping to the same object. * If there's any ordering to the pairing than we can probably just get away with only one * taskID mapping to it, leaving both for consistency with {@link #mSplitTasks} for now. */ private final Map<Integer, StagedSplitBounds> mTaskSplitBoundsMap = new HashMap<>(); /** * Creates {@link RecentTasksController}, returns {@code null} if the feature is not Loading Loading @@ -97,15 +107,20 @@ public class RecentTasksController implements TaskStackListenerCallback, /** * Adds a split pair. This call does not validate the taskIds, only that they are not the same. */ public void addSplitPair(int taskId1, int taskId2) { public void addSplitPair(int taskId1, int taskId2, StagedSplitBounds splitBounds) { if (taskId1 == taskId2) { return; } // Remove any previous pairs removeSplitPair(taskId1); removeSplitPair(taskId2); mTaskSplitBoundsMap.remove(taskId1); mTaskSplitBoundsMap.remove(taskId2); mSplitTasks.put(taskId1, taskId2); mSplitTasks.put(taskId2, taskId1); mTaskSplitBoundsMap.put(taskId1, splitBounds); mTaskSplitBoundsMap.put(taskId2, splitBounds); } /** Loading @@ -116,6 +131,8 @@ public class RecentTasksController implements TaskStackListenerCallback, if (pairedTaskId != INVALID_TASK_ID) { mSplitTasks.delete(taskId); mSplitTasks.delete(pairedTaskId); mTaskSplitBoundsMap.remove(taskId); mTaskSplitBoundsMap.remove(pairedTaskId); } } Loading Loading @@ -203,7 +220,8 @@ public class RecentTasksController implements TaskStackListenerCallback, if (pairedTaskId != INVALID_TASK_ID) { final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId); rawMapping.remove(pairedTaskId); recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo)); recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo, mTaskSplitBoundsMap.get(pairedTaskId))); } else { recentTasks.add(new GroupedRecentTaskInfo(taskInfo)); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +16 −1 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.StagedSplitBounds; import java.io.PrintWriter; import java.util.ArrayList; Loading Loading @@ -691,11 +692,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } mRecentTasks.ifPresent(recentTasks -> { Rect topLeftBounds = mSplitLayout.getBounds1(); Rect bottomRightBounds = mSplitLayout.getBounds2(); int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId(); int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId(); boolean sideStageTopLeft = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; int leftTopTaskId; int rightBottomTaskId; if (sideStageTopLeft) { leftTopTaskId = sideStageTopTaskId; rightBottomTaskId = mainStageTopTaskId; } else { leftTopTaskId = mainStageTopTaskId; rightBottomTaskId = sideStageTopTaskId; } StagedSplitBounds splitBounds = new StagedSplitBounds(topLeftBounds, bottomRightBounds, leftTopTaskId, rightBottomTaskId); if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) { // Update the pair for the top tasks recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId); recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds); } }); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java +13 −3 Original line number Diff line number Diff line Loading @@ -30,25 +30,34 @@ import androidx.annotation.Nullable; public class GroupedRecentTaskInfo implements Parcelable { public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1; public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2; public @Nullable StagedSplitBounds mStagedSplitBounds; public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) { this(task1, null); this(task1, null, null); } public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1, @Nullable ActivityManager.RecentTaskInfo task2) { @Nullable ActivityManager.RecentTaskInfo task2, @Nullable StagedSplitBounds stagedSplitBounds) { mTaskInfo1 = task1; mTaskInfo2 = task2; mStagedSplitBounds = stagedSplitBounds; } GroupedRecentTaskInfo(Parcel parcel) { mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR); mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR); mStagedSplitBounds = parcel.readTypedObject(StagedSplitBounds.CREATOR); } @Override public String toString() { return "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2); String taskString = "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2); if (mStagedSplitBounds != null) { taskString += ", SplitBounds: " + mStagedSplitBounds.toString(); } return taskString; } private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) { Loading @@ -67,6 +76,7 @@ public class GroupedRecentTaskInfo implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeTypedObject(mTaskInfo1, flags); parcel.writeTypedObject(mTaskInfo2, flags); parcel.writeTypedObject(mStagedSplitBounds, flags); } @Override Loading
libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java 0 → 100644 +114 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.util; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; /** * Container of various information needed to display split screen * tasks/leashes/etc in Launcher */ public class StagedSplitBounds implements Parcelable { public final Rect leftTopBounds; public final Rect rightBottomBounds; /** This rect represents the actual gap between the two apps */ public final Rect visualDividerBounds; // This class is orientation-agnostic, so we compute both for later use public final float topTaskPercent; public final float leftTaskPercent; /** * If {@code true}, that means at the time of creation of this object, the * split-screened apps were vertically stacked. This is useful in scenarios like * rotation where the bounds won't change, but this variable can indicate what orientation * the bounds were originally in */ public final boolean appsStackedVertically; public final int leftTopTaskId; public final int rightBottomTaskId; public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, int rightBottomTaskId) { this.leftTopBounds = leftTopBounds; this.rightBottomBounds = rightBottomBounds; this.leftTopTaskId = leftTopTaskId; this.rightBottomTaskId = rightBottomTaskId; if (rightBottomBounds.top > leftTopBounds.top) { // vertical apps, horizontal divider this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom, leftTopBounds.right, rightBottomBounds.top); appsStackedVertically = true; } else { // horizontal apps, vertical divider this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top, rightBottomBounds.left, leftTopBounds.bottom); appsStackedVertically = false; } leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right; topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom; } public StagedSplitBounds(Parcel parcel) { leftTopBounds = parcel.readTypedObject(Rect.CREATOR); rightBottomBounds = parcel.readTypedObject(Rect.CREATOR); visualDividerBounds = parcel.readTypedObject(Rect.CREATOR); topTaskPercent = parcel.readFloat(); leftTaskPercent = parcel.readFloat(); appsStackedVertically = parcel.readBoolean(); leftTopTaskId = parcel.readInt(); rightBottomTaskId = parcel.readInt(); } @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeTypedObject(leftTopBounds, flags); parcel.writeTypedObject(rightBottomBounds, flags); parcel.writeTypedObject(visualDividerBounds, flags); parcel.writeFloat(topTaskPercent); parcel.writeFloat(leftTaskPercent); parcel.writeBoolean(appsStackedVertically); parcel.writeInt(leftTopTaskId); parcel.writeInt(rightBottomTaskId); } @Override public int describeContents() { return 0; } @Override public String toString() { return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n" + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n" + "Divider: " + visualDividerBounds + "\n" + "AppsVertical? " + appsStackedVertically; } public static final Creator<StagedSplitBounds> CREATOR = new Creator<StagedSplitBounds>() { @Override public StagedSplitBounds createFromParcel(Parcel in) { return new StagedSplitBounds(in); } @Override public StagedSplitBounds[] newArray(int size) { return new StagedSplitBounds[size]; } }; }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +25 −6 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; Loading @@ -31,10 +33,9 @@ import static org.mockito.Mockito.verify; import static java.lang.Integer.MAX_VALUE; import android.app.ActivityManager; import android.app.WindowConfiguration; import android.content.Context; import android.graphics.Rect; import android.view.SurfaceControl; import android.window.TaskAppearedInfo; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -45,6 +46,7 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.StagedSplitBounds; import org.junit.Before; import org.junit.Test; Loading Loading @@ -106,8 +108,11 @@ public class RecentTasksControllerTest extends ShellTestCase { setRawList(t1, t2, t3, t4, t5, t6); // Mark a couple pairs [t2, t4], [t3, t5] mRecentTasksController.addSplitPair(t2.taskId, t4.taskId); mRecentTasksController.addSplitPair(t3.taskId, t5.taskId); StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 4); StagedSplitBounds pair2Bounds = new StagedSplitBounds(new Rect(), new Rect(), 3, 5); mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds); mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds); ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks( MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0); Loading @@ -126,7 +131,8 @@ public class RecentTasksControllerTest extends ShellTestCase { setRawList(t1, t2, t3); // Add a pair mRecentTasksController.addSplitPair(t2.taskId, t3.taskId); StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 3); mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds); reset(mRecentTasksController); // Remove one of the tasks and ensure the pair is removed Loading Loading @@ -201,10 +207,23 @@ public class RecentTasksControllerTest extends ShellTestCase { int[] flattenedTaskIds = new int[recentTasks.size() * 2]; for (int i = 0; i < recentTasks.size(); i++) { GroupedRecentTaskInfo pair = recentTasks.get(i); flattenedTaskIds[2 * i] = pair.mTaskInfo1.taskId; int taskId1 = pair.mTaskInfo1.taskId; flattenedTaskIds[2 * i] = taskId1; flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null ? pair.mTaskInfo2.taskId : -1; if (pair.mTaskInfo2 != null) { assertNotNull(pair.mStagedSplitBounds); int leftTopTaskId = pair.mStagedSplitBounds.leftTopTaskId; int bottomRightTaskId = pair.mStagedSplitBounds.rightBottomTaskId; // Unclear if pairs are ordered by split position, most likely not. assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId); assertTrue(bottomRightTaskId == taskId1 || bottomRightTaskId == pair.mTaskInfo2.taskId); } else { assertNull(pair.mStagedSplitBounds); } } assertTrue("Expected: " + Arrays.toString(expectedTaskIds) + " Received: " + Arrays.toString(flattenedTaskIds), Loading