Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +8 −0 Original line number Diff line number Diff line Loading @@ -502,16 +502,24 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) { wct.setBounds(task1.token, mBounds1); wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1)); mWinBounds1.set(mBounds1); mWinToken1 = task1.token; } if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) { wct.setBounds(task2.token, mBounds2); wct.setSmallestScreenWidthDp(task2.token, getSmallestWidthDp(mBounds2)); mWinBounds2.set(mBounds2); mWinToken2 = task2.token; } } private int getSmallestWidthDp(Rect bounds) { final int minWidth = Math.min(bounds.width(), bounds.height()); final float density = mContext.getResources().getDisplayMetrics().density; return (int) (minWidth / density); } /** * Shift configuration bounds to prevent client apps get configuration changed or relaunch. And * restore shifted configuration bounds if it's no longer shifted. Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +4 −6 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen; import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; Loading Loading @@ -49,14 +48,13 @@ class MainStage extends StageTaskListener { return mIsActive; } void activate(Rect rootBounds, WindowContainerTransaction wct, boolean includingTopTask) { void activate(WindowContainerTransaction wct, boolean includingTopTask) { if (mIsActive) return; final WindowContainerToken rootToken = mRootTaskInfo.token; wct.setBounds(rootToken, rootBounds) // Moving the root task to top after the child tasks were re-parented , or the root // task cannot be visible and focused. .reorder(rootToken, true /* onTop */); wct.reorder(rootToken, true /* onTop */); if (includingTopTask) { wct.reparentTasks( null /* currentParent */, Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +10 −9 Original line number Diff line number Diff line Loading @@ -354,8 +354,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setDivideRatio(splitRatio); // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(getMainStageBounds(), wct, false /* reparent */); mSideStage.setBounds(getSideStageBounds(), wct); mMainStage.activate(wct, false /* reparent */); updateWindowBounds(mSplitLayout, wct); // Make sure the launch options will put tasks in the corresponding split roots addActivityOptions(mainOptions, mMainStage); Loading Loading @@ -470,13 +470,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setDivideRatio(splitRatio); if (mMainStage.isActive()) { mMainStage.moveToTop(getMainStageBounds(), wct); mMainStage.moveToTop(wct); } else { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(getMainStageBounds(), wct, false /* reparent */); mMainStage.activate(wct, false /* reparent */); } mSideStage.moveToTop(getSideStageBounds(), wct); mSideStage.moveToTop(wct); updateWindowBounds(mSplitLayout, wct); // Make sure the launch options will put tasks in the corresponding split roots addActivityOptions(mainOptions, mMainStage); Loading Loading @@ -774,8 +775,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(startPosition, wct); mSideStage.addTask(taskInfo, wct); } mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */); mSideStage.moveToTop(getSideStageBounds(), wct); mMainStage.activate(wct, true /* includingTopTask */); mSideStage.moveToTop(wct); updateWindowBounds(mSplitLayout, wct); } void finishEnterSplitScreen(SurfaceControl.Transaction t) { Loading Loading @@ -997,10 +999,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Exit to side stage if main stage no longer has children. exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED); } } else if (isSideStage) { } else if (isSideStage && !mMainStage.isActive()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mSplitLayout.init(); // Make sure the main stage is active. prepareEnterSplitScreen(wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +4 −8 Original line number Diff line number Diff line Loading @@ -53,8 +53,8 @@ import java.io.PrintWriter; * Base class that handle common task org. related for split-screen stages. * Note that this class and its sub-class do not directly perform hierarchy operations. * They only serve to hold a collection of tasks and provide APIs like * {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator} * to perform operations in-sync with other containers. * {@link #addTask(ActivityManager.RunningTaskInfo, WindowContainerTransaction)} for the centralized * {@link StageCoordinator} to perform hierarchy operations in-sync with other containers. * * @see StageCoordinator */ Loading Loading @@ -310,13 +310,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/); } void moveToTop(Rect rootBounds, WindowContainerTransaction wct) { void moveToTop(WindowContainerTransaction wct) { final WindowContainerToken rootToken = mRootTaskInfo.token; wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */); } void setBounds(Rect bounds, WindowContainerTransaction wct) { wct.setBounds(mRootTaskInfo.token, bounds); wct.reorder(rootToken, true /* onTop */); } void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) { Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +14 −0 Original line number Diff line number Diff line Loading @@ -28,8 +28,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.content.res.Configuration; import android.graphics.Rect; import android.window.WindowContainerTransaction; import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; Loading @@ -38,6 +40,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; Loading @@ -56,6 +59,7 @@ public class SplitLayoutTests extends ShellTestCase { @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks; @Mock DisplayImeController mDisplayImeController; @Mock ShellTaskOrganizer mTaskOrganizer; @Mock WindowContainerTransaction mWct; @Captor ArgumentCaptor<Runnable> mRunnableCaptor; private SplitLayout mSplitLayout; Loading Loading @@ -149,6 +153,16 @@ public class SplitLayoutTests extends ShellTestCase { verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true)); } @Test public void testApplyTaskChanges_updatesSmallestScreenWidthDp() { final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build(); final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build(); mSplitLayout.applyTaskChanges(mWct, task1, task2); verify(mWct).setSmallestScreenWidthDp(eq(task1.token), anyInt()); verify(mWct).setSmallestScreenWidthDp(eq(task2.token), anyInt()); } private void waitDividerFlingFinished() { verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture()); mRunnableCaptor.getValue().run(); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +8 −0 Original line number Diff line number Diff line Loading @@ -502,16 +502,24 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) { wct.setBounds(task1.token, mBounds1); wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1)); mWinBounds1.set(mBounds1); mWinToken1 = task1.token; } if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) { wct.setBounds(task2.token, mBounds2); wct.setSmallestScreenWidthDp(task2.token, getSmallestWidthDp(mBounds2)); mWinBounds2.set(mBounds2); mWinToken2 = task2.token; } } private int getSmallestWidthDp(Rect bounds) { final int minWidth = Math.min(bounds.width(), bounds.height()); final float density = mContext.getResources().getDisplayMetrics().density; return (int) (minWidth / density); } /** * Shift configuration bounds to prevent client apps get configuration changed or relaunch. And * restore shifted configuration bounds if it's no longer shifted. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +4 −6 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen; import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; Loading Loading @@ -49,14 +48,13 @@ class MainStage extends StageTaskListener { return mIsActive; } void activate(Rect rootBounds, WindowContainerTransaction wct, boolean includingTopTask) { void activate(WindowContainerTransaction wct, boolean includingTopTask) { if (mIsActive) return; final WindowContainerToken rootToken = mRootTaskInfo.token; wct.setBounds(rootToken, rootBounds) // Moving the root task to top after the child tasks were re-parented , or the root // task cannot be visible and focused. .reorder(rootToken, true /* onTop */); wct.reorder(rootToken, true /* onTop */); if (includingTopTask) { wct.reparentTasks( null /* currentParent */, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +10 −9 Original line number Diff line number Diff line Loading @@ -354,8 +354,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setDivideRatio(splitRatio); // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(getMainStageBounds(), wct, false /* reparent */); mSideStage.setBounds(getSideStageBounds(), wct); mMainStage.activate(wct, false /* reparent */); updateWindowBounds(mSplitLayout, wct); // Make sure the launch options will put tasks in the corresponding split roots addActivityOptions(mainOptions, mMainStage); Loading Loading @@ -470,13 +470,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setDivideRatio(splitRatio); if (mMainStage.isActive()) { mMainStage.moveToTop(getMainStageBounds(), wct); mMainStage.moveToTop(wct); } else { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(getMainStageBounds(), wct, false /* reparent */); mMainStage.activate(wct, false /* reparent */); } mSideStage.moveToTop(getSideStageBounds(), wct); mSideStage.moveToTop(wct); updateWindowBounds(mSplitLayout, wct); // Make sure the launch options will put tasks in the corresponding split roots addActivityOptions(mainOptions, mMainStage); Loading Loading @@ -774,8 +775,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(startPosition, wct); mSideStage.addTask(taskInfo, wct); } mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */); mSideStage.moveToTop(getSideStageBounds(), wct); mMainStage.activate(wct, true /* includingTopTask */); mSideStage.moveToTop(wct); updateWindowBounds(mSplitLayout, wct); } void finishEnterSplitScreen(SurfaceControl.Transaction t) { Loading Loading @@ -997,10 +999,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Exit to side stage if main stage no longer has children. exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED); } } else if (isSideStage) { } else if (isSideStage && !mMainStage.isActive()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mSplitLayout.init(); // Make sure the main stage is active. prepareEnterSplitScreen(wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +4 −8 Original line number Diff line number Diff line Loading @@ -53,8 +53,8 @@ import java.io.PrintWriter; * Base class that handle common task org. related for split-screen stages. * Note that this class and its sub-class do not directly perform hierarchy operations. * They only serve to hold a collection of tasks and provide APIs like * {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator} * to perform operations in-sync with other containers. * {@link #addTask(ActivityManager.RunningTaskInfo, WindowContainerTransaction)} for the centralized * {@link StageCoordinator} to perform hierarchy operations in-sync with other containers. * * @see StageCoordinator */ Loading Loading @@ -310,13 +310,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/); } void moveToTop(Rect rootBounds, WindowContainerTransaction wct) { void moveToTop(WindowContainerTransaction wct) { final WindowContainerToken rootToken = mRootTaskInfo.token; wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */); } void setBounds(Rect bounds, WindowContainerTransaction wct) { wct.setBounds(mRootTaskInfo.token, bounds); wct.reorder(rootToken, true /* onTop */); } void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) { Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +14 −0 Original line number Diff line number Diff line Loading @@ -28,8 +28,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.content.res.Configuration; import android.graphics.Rect; import android.window.WindowContainerTransaction; import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; Loading @@ -38,6 +40,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; Loading @@ -56,6 +59,7 @@ public class SplitLayoutTests extends ShellTestCase { @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks; @Mock DisplayImeController mDisplayImeController; @Mock ShellTaskOrganizer mTaskOrganizer; @Mock WindowContainerTransaction mWct; @Captor ArgumentCaptor<Runnable> mRunnableCaptor; private SplitLayout mSplitLayout; Loading Loading @@ -149,6 +153,16 @@ public class SplitLayoutTests extends ShellTestCase { verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true)); } @Test public void testApplyTaskChanges_updatesSmallestScreenWidthDp() { final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build(); final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build(); mSplitLayout.applyTaskChanges(mWct, task1, task2); verify(mWct).setSmallestScreenWidthDp(eq(task1.token), anyInt()); verify(mWct).setSmallestScreenWidthDp(eq(task2.token), anyInt()); } private void waitDividerFlingFinished() { verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture()); mRunnableCaptor.getValue().run(); Loading