From 6c75e58fc6be88e7ef9f7b7cac9beee495831890 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 20 Jan 2021 23:28:56 -0800 Subject: [PATCH 1/2] 9/ Update transitions/starting window for shell thread Bug: 161979899 Test: atest WMShellUnitTests Test: atest SystemUITests Change-Id: I0afbacd0db32ef02f1e178f7a53b16df5b64449b --- .../StartingSurfaceDrawer.java | 70 +++++++++---------- .../startingsurface/TaskSnapshotWindow.java | 56 +++++++-------- .../shell/transition/RemoteTransitions.java | 40 +++++++++++ .../wm/shell/transition/Transitions.java | 45 ++++++++++-- .../TaskSnapshotWindowTest.java | 3 +- .../systemui/dagger/SysUIComponent.java | 4 +- .../android/systemui/dagger/WMComponent.java | 9 +-- .../recents/OverviewProxyService.java | 6 +- .../systemui/wmshell/WMShellBaseModule.java | 7 ++ .../recents/OverviewProxyServiceTest.java | 4 +- 10 files changed, 157 insertions(+), 87 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 8e24e0b516cb5..f3749220d4e1a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -377,12 +377,10 @@ public class StartingSurfaceDrawer { final int taskId = startingWindowInfo.taskInfo.taskId; final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken, snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */); - mMainExecutor.execute(() -> { - mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT); - final StartingWindowRecord tView = - new StartingWindowRecord(null/* decorView */, surface); - mStartingWindowRecords.put(taskId, tView); - }); + mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT); + final StartingWindowRecord tView = + new StartingWindowRecord(null/* decorView */, surface); + mStartingWindowRecords.put(taskId, tView); } /** @@ -392,42 +390,40 @@ public class StartingSurfaceDrawer { if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId); } - mMainExecutor.execute(() -> removeWindowSynced(taskId)); + removeWindowSynced(taskId); } protected void postAddWindow(int taskId, IBinder appToken, - View view, WindowManager wm, WindowManager.LayoutParams params) { - mMainExecutor.execute(() -> { - boolean shouldSaveView = true; - try { - wm.addView(view, params); - } catch (WindowManager.BadTokenException e) { - // ignore - Slog.w(TAG, appToken + " already running, starting window not displayed. " - + e.getMessage()); + View view, WindowManager wm, WindowManager.LayoutParams params) { + boolean shouldSaveView = true; + try { + wm.addView(view, params); + } catch (WindowManager.BadTokenException e) { + // ignore + Slog.w(TAG, appToken + " already running, starting window not displayed. " + + e.getMessage()); + shouldSaveView = false; + } catch (RuntimeException e) { + // don't crash if something else bad happens, for example a + // failure loading resources because we are loading from an app + // on external storage that has been unmounted. + Slog.w(TAG, appToken + " failed creating starting window", e); + shouldSaveView = false; + } finally { + if (view != null && view.getParent() == null) { + Slog.w(TAG, "view not successfully added to wm, removing view"); + wm.removeViewImmediate(view); shouldSaveView = false; - } catch (RuntimeException e) { - // don't crash if something else bad happens, for example a - // failure loading resources because we are loading from an app - // on external storage that has been unmounted. - Slog.w(TAG, appToken + " failed creating starting window", e); - shouldSaveView = false; - } finally { - if (view != null && view.getParent() == null) { - Slog.w(TAG, "view not successfully added to wm, removing view"); - wm.removeViewImmediate(view); - shouldSaveView = false; - } } + } - if (shouldSaveView) { - removeWindowSynced(taskId); - mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT); - final StartingWindowRecord tView = - new StartingWindowRecord(view, null /* TaskSnapshotWindow */); - mStartingWindowRecords.put(taskId, tView); - } - }); + if (shouldSaveView) { + removeWindowSynced(taskId); + mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT); + final StartingWindowRecord tView = + new StartingWindowRecord(view, null /* TaskSnapshotWindow */); + mStartingWindowRecords.put(taskId, tView); + } } protected void removeWindowSynced(int taskId) { @@ -445,7 +441,7 @@ public class StartingSurfaceDrawer { if (DEBUG_TASK_SNAPSHOT) { Slog.v(TAG, "Removing task snapshot window for " + taskId); } - record.mTaskSnapshotWindow.remove(mMainExecutor); + record.mTaskSnapshotWindow.remove(); } mStartingWindowRecords.remove(taskId); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index b5e18960ff5ce..a6f44efd76458 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -82,6 +82,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.DecorView; import com.android.internal.view.BaseIWindow; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ExternalThread; /** * This class represents a starting window that shows a snapshot. @@ -121,6 +122,7 @@ public class TaskSnapshotWindow { private final Window mWindow; private final Surface mSurface; private final Runnable mClearWindowHandler; + private final ShellExecutor mMainExecutor; private SurfaceControl mSurfaceControl; private SurfaceControl mChildSurfaceControl; private final IWindowSession mSession; @@ -213,7 +215,7 @@ public class TaskSnapshotWindow { final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance, windowFlags, windowPrivateFlags, taskBounds, orientation, activityType, - topWindowInsetsState, clearWindowHandler); + topWindowInsetsState, clearWindowHandler, mainExecutor); final Window window = snapshotSurface.mWindow; final InsetsState mTmpInsetsState = new InsetsState(); @@ -229,7 +231,7 @@ public class TaskSnapshotWindow { } catch (RemoteException e) { snapshotSurface.clearWindowSynced(); } - window.setOuter(snapshotSurface, mainExecutor); + window.setOuter(snapshotSurface); try { session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1, tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState, @@ -249,7 +251,8 @@ public class TaskSnapshotWindow { TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds, int currentOrientation, int activityType, InsetsState topWindowInsetsState, - Runnable clearWindowHandler) { + Runnable clearWindowHandler, ShellExecutor mainExecutor) { + mMainExecutor = mainExecutor; mSurface = new Surface(); mSession = WindowManagerGlobal.getWindowSession(); mWindow = new Window(); @@ -286,28 +289,26 @@ public class TaskSnapshotWindow { mSystemBarBackgroundPainter.drawNavigationBarBackground(c); } - void remove(ShellExecutor mainExecutor) { + void remove() { final long now = SystemClock.uptimeMillis(); if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS // Show the latest content as soon as possible for unlocking to home. && mActivityType != ACTIVITY_TYPE_HOME) { final long delayTime = mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS - now; - mainExecutor.executeDelayed(() -> remove(mainExecutor), delayTime); + mMainExecutor.executeDelayed(() -> remove(), delayTime); if (DEBUG) { Slog.d(TAG, "Defer removing snapshot surface in " + delayTime); } return; } - mainExecutor.execute(() -> { - try { - if (DEBUG) { - Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn); - } - mSession.remove(mWindow); - } catch (RemoteException e) { - // nothing + try { + if (DEBUG) { + Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn); } - }); + mSession.remove(mWindow); + } catch (RemoteException e) { + // nothing + } } /** @@ -497,13 +498,12 @@ public class TaskSnapshotWindow { } } + @ExternalThread static class Window extends BaseIWindow { private TaskSnapshotWindow mOuter; - private ShellExecutor mMainExecutor; - public void setOuter(TaskSnapshotWindow outer, ShellExecutor mainExecutor) { + public void setOuter(TaskSnapshotWindow outer) { mOuter = outer; - mMainExecutor = mainExecutor; } @Override @@ -511,22 +511,20 @@ public class TaskSnapshotWindow { MergedConfiguration mergedConfiguration, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId) { if (mOuter != null) { - if (mergedConfiguration != null - && mOuter.mOrientationOnCreation - != mergedConfiguration.getMergedConfiguration().orientation) { - // The orientation of the screen is changing. We better remove the snapshot ASAP - // as we are going to wait on the new window in any case to unfreeze the screen, - // and the starting window is not needed anymore. - mMainExecutor.execute(() -> { + mOuter.mMainExecutor.execute(() -> { + if (mergedConfiguration != null + && mOuter.mOrientationOnCreation + != mergedConfiguration.getMergedConfiguration().orientation) { + // The orientation of the screen is changing. We better remove the snapshot + // ASAP as we are going to wait on the new window in any case to unfreeze + // the screen, and the starting window is not needed anymore. mOuter.clearWindowSynced(); - }); - } else if (reportDraw) { - mMainExecutor.execute(() -> { + } else if (reportDraw) { if (mOuter.mHasDrawn) { mOuter.reportDrawn(); } - }); - } + } + }); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java new file mode 100644 index 0000000000000..85bbf74d56b98 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java @@ -0,0 +1,40 @@ +/* + * 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.transition; + +import android.annotation.NonNull; +import android.window.IRemoteTransition; +import android.window.TransitionFilter; + +import com.android.wm.shell.common.annotations.ExternalThread; + +/** + * Interface to manage remote transitions. + */ +@ExternalThread +public interface RemoteTransitions { + /** + * Registers a remote transition. + */ + void registerRemote(@NonNull TransitionFilter filter, + @NonNull IRemoteTransition remoteTransition); + + /** + * Unregisters a remote transition. + */ + void unregisterRemote(@NonNull IRemoteTransition remoteTransition); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 2ab4e0bdd76f1..0e171f6b41a35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -67,6 +67,7 @@ public class Transitions { private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; private final RemoteTransitionHandler mRemoteTransitionHandler; + private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl(); /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList mHandlers = new ArrayList<>(); @@ -78,6 +79,10 @@ public class Transitions { /** Keeps track of currently tracked transitions and all the animations associated with each */ private final ArrayMap mActiveTransitions = new ArrayMap<>(); + public static RemoteTransitions asRemoteTransitions(Transitions transitions) { + return transitions.mImpl; + } + public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { mOrganizer = organizer; @@ -101,8 +106,20 @@ public class Transitions { /** Create an empty/non-registering transitions object for system-ui tests. */ @VisibleForTesting - public static Transitions createEmptyForTesting() { - return new Transitions(); + public static RemoteTransitions createEmptyForTesting() { + return new RemoteTransitions() { + @Override + public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter, + @androidx.annotation.NonNull IRemoteTransition remoteTransition) { + // Do nothing + } + + @Override + public void unregisterRemote( + @androidx.annotation.NonNull IRemoteTransition remoteTransition) { + // Do nothing + } + }; } /** Register this transition handler with Core */ @@ -134,16 +151,14 @@ public class Transitions { } /** Register a remote transition to be used when `filter` matches an incoming transition */ - @ExternalThread public void registerRemote(@NonNull TransitionFilter filter, @NonNull IRemoteTransition remoteTransition) { - mMainExecutor.execute(() -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition)); + mRemoteTransitionHandler.addFiltered(filter, remoteTransition); } /** Unregisters a remote transition and all associated filters */ - @ExternalThread public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) { - mMainExecutor.execute(() -> mRemoteTransitionHandler.removeFiltered(remoteTransition)); + mRemoteTransitionHandler.removeFiltered(remoteTransition); } /** @return true if the transition was triggered by opening something vs closing something */ @@ -361,4 +376,22 @@ public class Transitions { mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request)); } } + + @ExternalThread + private class RemoteTransitionImpl implements RemoteTransitions { + @Override + public void registerRemote(@NonNull TransitionFilter filter, + @NonNull IRemoteTransition remoteTransition) { + mMainExecutor.execute(() -> { + Transitions.this.registerRemote(filter, remoteTransition); + }); + } + + @Override + public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) { + mMainExecutor.execute(() -> { + Transitions.this.unregisterRemote(remoteTransition); + }); + } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java index 414a0a778d937..27e5f51d88b85 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java @@ -47,6 +47,7 @@ import android.window.TaskSnapshot; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.startingsurface.TaskSnapshotWindow; import org.junit.Test; @@ -83,7 +84,7 @@ public class TaskSnapshotWindowTest { createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), 0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */, taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState(), - null /* clearWindow */); + null /* clearWindow */, new TestShellExecutor()); } private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 062410abb689b..ffb8446f3e21a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -33,7 +33,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; -import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.util.Optional; @@ -86,7 +86,7 @@ public interface SysUIComponent { Builder setShellCommandHandler(Optional shellDump); @BindsInstance - Builder setTransitions(Transitions t); + Builder setTransitions(RemoteTransitions t); SysUIComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 60b665f0a51a7..84dd25963a15f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -27,7 +27,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; -import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.util.Optional; @@ -55,16 +55,12 @@ public interface WMComponent { getShellInit().init(); } - // Gets the Shell init instance @WMSingleton ShellInit getShellInit(); - // Gets the Shell dump instance @WMSingleton Optional getShellCommandHandler(); - // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change - // depending on the threading mechanism we go with @WMSingleton Optional getOneHanded(); @@ -89,7 +85,6 @@ public interface WMComponent { @WMSingleton Optional getTaskViewFactory(); - /** Gets transitions */ @WMSingleton - Transitions getTransitions(); + RemoteTransitions getTransitions(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index a9f76f61c5379..01a8c1c89f840 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -101,7 +101,7 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.splitscreen.SplitScreen; -import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -149,7 +149,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final ScreenshotHelper mScreenshotHelper; private final Optional mOneHandedOptional; private final CommandQueue mCommandQueue; - private final Transitions mShellTransitions; + private final RemoteTransitions mShellTransitions; private Region mActiveNavBarRegion; @@ -799,7 +799,7 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional> statusBarOptionalLazy, Optional oneHandedOptional, BroadcastDispatcher broadcastDispatcher, - Transitions shellTransitions) { + RemoteTransitions shellTransitions) { super(broadcastDispatcher); mContext = context; mPipOptional = pipOptional; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index b7aa907c6180d..36506bf5715cc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -75,6 +75,7 @@ import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.transition.RemoteTransitions; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -386,6 +387,12 @@ public abstract class WMShellBaseModule { return new FullscreenTaskListener(syncQueue); } + @WMSingleton + @Provides + static RemoteTransitions provideRemoteTransitions(Transitions transitions) { + return Transitions.asRemoteTransitions(transitions); + } + @WMSingleton @Provides static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java index 451c78fd1202c..6d2b8e415e969 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java @@ -43,7 +43,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; -import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.transition.RemoteTransitions; import org.junit.Before; import org.junit.Test; @@ -78,7 +78,7 @@ public class OverviewProxyServiceTest extends SysuiTestCase { @Mock private Optional mMockOneHandedOptional; @Mock private PackageManager mPackageManager; @Mock private SysUiState mMockSysUiState; - @Mock private Transitions mMockTransitions; + @Mock private RemoteTransitions mMockTransitions; @Before public void setUp() throws RemoteException { From 0054d7fc8c03074e5ecf2d6e2bd2f607462c6190 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 5 Feb 2021 00:28:15 -0800 Subject: [PATCH 2/2] 10/ Update split for shell thread - Also remove usages of external interface within the shell code and remove methods only used in the controller from the interface (will update for other components separately) Bug: 161979899 Test: atest WMShellUnitTests Test: atest SystemUITests Change-Id: I06d266095d8e287ba131e41dc6bebefa15c9e477 --- .../wm/shell/ShellCommandHandlerImpl.java | 7 +- .../com/android/wm/shell/ShellInitImpl.java | 9 +- .../draganddrop/DragAndDropController.java | 5 +- .../shell/draganddrop/DragAndDropPolicy.java | 70 +++---- .../wm/shell/draganddrop/DragLayout.java | 4 +- .../wm/shell/splitscreen/SplitScreen.java | 10 +- .../splitscreen/SplitScreenController.java | 178 +++++++++++++++--- .../draganddrop/DragAndDropPolicyTest.java | 53 +++--- .../systemui/wmshell/WMShellBaseModule.java | 19 +- 9 files changed, 241 insertions(+), 114 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java index fe97e24fac416..982cc006e331c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -25,6 +25,7 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.io.PrintWriter; import java.util.Optional; @@ -38,7 +39,7 @@ public final class ShellCommandHandlerImpl { private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName(); private final Optional mLegacySplitScreenOptional; - private final Optional mSplitScreenOptional; + private final Optional mSplitScreenOptional; private final Optional mPipOptional; private final Optional mOneHandedOptional; private final Optional mHideDisplayCutout; @@ -50,7 +51,7 @@ public final class ShellCommandHandlerImpl { public static ShellCommandHandler create( ShellTaskOrganizer shellTaskOrganizer, Optional legacySplitScreenOptional, - Optional splitScreenOptional, + Optional splitScreenOptional, Optional pipOptional, Optional oneHandedOptional, Optional hideDisplayCutout, @@ -64,7 +65,7 @@ public final class ShellCommandHandlerImpl { private ShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, Optional legacySplitScreenOptional, - Optional splitScreenOptional, + Optional splitScreenOptional, Optional pipOptional, Optional oneHandedOptional, Optional hideDisplayCutout, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 0958a070c82df..925bf4bbb01c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -25,6 +25,7 @@ import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -39,7 +40,7 @@ public class ShellInitImpl { private final DragAndDropController mDragAndDropController; private final ShellTaskOrganizer mShellTaskOrganizer; private final Optional mLegacySplitScreenOptional; - private final Optional mSplitScreenOptional; + private final Optional mSplitScreenOptional; private final Optional mAppPairsOptional; private final FullscreenTaskListener mFullscreenTaskListener; private final ShellExecutor mMainExecutor; @@ -51,7 +52,7 @@ public class ShellInitImpl { DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, Optional legacySplitScreenOptional, - Optional splitScreenOptional, + Optional splitScreenOptional, Optional appPairsOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, @@ -71,7 +72,7 @@ public class ShellInitImpl { DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, Optional legacySplitScreenOptional, - Optional splitScreenOptional, + Optional splitScreenOptional, Optional appPairsOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, @@ -97,7 +98,7 @@ public class ShellInitImpl { mShellTaskOrganizer.registerOrganizer(); mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered); - mSplitScreenOptional.ifPresent(SplitScreen::onOrganizerRegistered); + mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered); // Bind the splitscreen impl to the drag drop controller mDragAndDropController.initialize(mSplitScreenOptional); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index c8938ad40aba9..17709438baba0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -53,6 +53,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.Optional; @@ -66,7 +67,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange private final Context mContext; private final DisplayController mDisplayController; - private SplitScreen mSplitScreen; + private SplitScreenController mSplitScreen; private final SparseArray mDisplayDropTargets = new SparseArray<>(); private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); @@ -76,7 +77,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mDisplayController = displayController; } - public void initialize(Optional splitscreen) { + public void initialize(Optional splitscreen) { mSplitScreen = splitscreen.orElse(null); mDisplayController.addDisplayWindowListener(this); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 35dcdd5923a80..6f5f2eb5723c9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -64,7 +64,9 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreen.StagePosition; +import com.android.wm.shell.splitscreen.SplitScreen.StageType; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -81,18 +83,18 @@ public class DragAndDropPolicy { private final Context mContext; private final ActivityTaskManager mActivityTaskManager; private final Starter mStarter; - private final SplitScreen mSplitScreen; + private final SplitScreenController mSplitScreen; private final ArrayList mTargets = new ArrayList<>(); private DragSession mSession; - public DragAndDropPolicy(Context context, SplitScreen splitScreen) { + public DragAndDropPolicy(Context context, SplitScreenController splitScreen) { this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context)); } @VisibleForTesting DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager, - SplitScreen splitScreen, Starter starter) { + SplitScreenController splitScreen, Starter starter) { mContext = context; mActivityTaskManager = activityTaskManager; mSplitScreen = splitScreen; @@ -200,8 +202,8 @@ public class DragAndDropPolicy { final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible(); final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT; - @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED; - @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED; + @StageType int stage = STAGE_TYPE_UNDEFINED; + @StagePosition int position = STAGE_POSITION_UNDEFINED; if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) { // Update launch options for the split side we are targeting. position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT; @@ -213,7 +215,28 @@ public class DragAndDropPolicy { final ClipDescription description = data.getDescription(); final Intent dragData = mSession.dragData; - mStarter.startClipDescription(description, dragData, stage, position); + startClipDescription(description, dragData, stage, position); + } + + private void startClipDescription(ClipDescription description, Intent intent, + @StageType int stage, @StagePosition int position) { + final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK); + final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); + final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS) + ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle(); + + if (isTask) { + final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID); + mStarter.startTask(taskId, stage, position, opts); + } else if (isShortcut) { + final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); + final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID); + final UserHandle user = intent.getParcelableExtra(EXTRA_USER); + mStarter.startShortcut(packageName, id, stage, position, opts, user); + } else { + mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, + opts); + } } /** @@ -267,34 +290,13 @@ public class DragAndDropPolicy { /** * Interface for actually committing the task launches. */ - @VisibleForTesting public interface Starter { - default void startClipDescription(ClipDescription description, Intent intent, - @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) { - final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK); - final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); - final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS) - ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle(); - - if (isTask) { - final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID); - startTask(taskId, stage, position, opts); - } else if (isShortcut) { - final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); - final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID); - final UserHandle user = intent.getParcelableExtra(EXTRA_USER); - startShortcut(packageName, id, stage, position, opts, user); - } else { - startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts); - } - } - void startTask(int taskId, @SplitScreen.StageType int stage, - @SplitScreen.StagePosition int position, @Nullable Bundle options); - void startShortcut(String packageName, String shortcutId, - @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position, - @Nullable Bundle options, UserHandle user); - void startIntent(PendingIntent intent, @SplitScreen.StageType int stage, - @SplitScreen.StagePosition int position, @Nullable Bundle options); + void startTask(int taskId, @StageType int stage, @StagePosition int position, + @Nullable Bundle options); + void startShortcut(String packageName, String shortcutId, @StageType int stage, + @StagePosition int position, @Nullable Bundle options, UserHandle user); + void startIntent(PendingIntent intent, @StageType int stage, @StagePosition int position, + @Nullable Bundle options); void enterSplitScreen(int taskId, boolean leftOrTop); void exitSplitScreen(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 82c4e440fb159..b3423362347fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -42,7 +42,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.protolog.ShellProtoLogGroup; -import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.ArrayList; @@ -61,7 +61,7 @@ public class DragLayout extends View { private boolean mIsShowing; private boolean mHasDropped; - public DragLayout(Context context, SplitScreen splitscreen) { + public DragLayout(Context context, SplitScreenController splitscreen) { super(context); mPolicy = new DragAndDropPolicy(context, splitscreen); mDisplayMargin = context.getResources().getDimensionPixelSize( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index 2c6809259459b..177646b22ea39 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -33,6 +33,7 @@ import java.io.PrintWriter; /** * Interface to engage split-screen feature. + * TODO: Figure out which of these are actually needed outside of the Shell */ @ExternalThread public interface SplitScreen extends DragAndDropPolicy.Starter { @@ -102,18 +103,11 @@ public interface SplitScreen extends DragAndDropPolicy.Starter { void setSideStagePosition(@StagePosition int sideStagePosition); /** Hides the side-stage if it is currently visible. */ void setSideStageVisibility(boolean visible); - default void enterSplitScreen(int taskId, boolean leftOrTop) { - moveToSideStage(taskId, - leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT); - } + /** Removes the split-screen stages. */ void exitSplitScreen(); /** Gets the stage bounds. */ void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds); - /** Dumps current status of split-screen. */ - void dump(@NonNull PrintWriter pw, String prefix); - /** Called when the shell organizer has been registered. */ - void onOrganizerRegistered(); void registerSplitScreenListener(SplitScreenListener listener); void unregisterSplitScreenListener(SplitScreenListener listener); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 18dd53b90ff44..bbad36dcc0469 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -18,6 +18,13 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; + import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PendingIntent; @@ -35,7 +42,9 @@ import androidx.annotation.Nullable; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.draganddrop.DragAndDropPolicy; import java.io.PrintWriter; @@ -44,25 +53,33 @@ import java.io.PrintWriter; * {@link SplitScreen}. * @see StageCoordinator */ -public class SplitScreenController implements SplitScreen { +public class SplitScreenController implements DragAndDropPolicy.Starter { private static final String TAG = SplitScreenController.class.getSimpleName(); private final ShellTaskOrganizer mTaskOrganizer; private final SyncTransactionQueue mSyncQueue; private final Context mContext; private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; + private final ShellExecutor mMainExecutor; + private final SplitScreenImpl mImpl = new SplitScreenImpl(); + private StageCoordinator mStageCoordinator; public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, - RootTaskDisplayAreaOrganizer rootTDAOrganizer) { + RootTaskDisplayAreaOrganizer rootTDAOrganizer, + ShellExecutor mainExecutor) { mTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mContext = context; mRootTDAOrganizer = rootTDAOrganizer; + mMainExecutor = mainExecutor; + } + + public SplitScreen asSplitScreen() { + return mImpl; } - @Override public void onOrganizerRegistered() { if (mStageCoordinator == null) { // TODO: Multi-display @@ -71,13 +88,11 @@ public class SplitScreenController implements SplitScreen { } } - @Override public boolean isSplitScreenVisible() { return mStageCoordinator.isSplitScreenVisible(); } - @Override - public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) { + public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) { final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId); if (task == null) { throw new IllegalArgumentException("Unknown taskId" + taskId); @@ -85,50 +100,46 @@ public class SplitScreenController implements SplitScreen { return moveToSideStage(task, sideStagePosition); } - @Override public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, - @StagePosition int sideStagePosition) { + @SplitScreen.StagePosition int sideStagePosition) { return mStageCoordinator.moveToSideStage(task, sideStagePosition); } - @Override public boolean removeFromSideStage(int taskId) { return mStageCoordinator.removeFromSideStage(taskId); } - @Override - public void setSideStagePosition(@StagePosition int sideStagePosition) { + public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) { mStageCoordinator.setSideStagePosition(sideStagePosition); } - @Override public void setSideStageVisibility(boolean visible) { mStageCoordinator.setSideStageVisibility(visible); } - @Override + public void enterSplitScreen(int taskId, boolean leftOrTop) { + moveToSideStage(taskId, + leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT); + } + public void exitSplitScreen() { mStageCoordinator.exitSplitScreen(); } - @Override public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds); } - @Override - public void registerSplitScreenListener(SplitScreenListener listener) { + public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) { mStageCoordinator.registerSplitScreenListener(listener); } - @Override - public void unregisterSplitScreenListener(SplitScreenListener listener) { + public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) { mStageCoordinator.unregisterSplitScreenListener(listener); } - @Override - public void startTask(int taskId, - @StageType int stage, @StagePosition int position, @Nullable Bundle options) { + public void startTask(int taskId, @SplitScreen.StageType int stage, + @SplitScreen.StagePosition int position, @Nullable Bundle options) { options = resolveStartStage(stage, position, options); try { @@ -138,9 +149,9 @@ public class SplitScreenController implements SplitScreen { } } - @Override - public void startShortcut(String packageName, String shortcutId, @StageType int stage, - @StagePosition int position, @Nullable Bundle options, UserHandle user) { + public void startShortcut(String packageName, String shortcutId, + @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position, + @Nullable Bundle options, UserHandle user) { options = resolveStartStage(stage, position, options); try { @@ -153,9 +164,8 @@ public class SplitScreenController implements SplitScreen { } } - @Override - public void startIntent(PendingIntent intent, - @StageType int stage, @StagePosition int position, @Nullable Bundle options) { + public void startIntent(PendingIntent intent, @SplitScreen.StageType int stage, + @SplitScreen.StagePosition int position, @Nullable Bundle options) { options = resolveStartStage(stage, position, options); try { @@ -165,8 +175,8 @@ public class SplitScreenController implements SplitScreen { } } - private Bundle resolveStartStage(@StageType int stage, @StagePosition int position, - @Nullable Bundle options) { + private Bundle resolveStartStage(@SplitScreen.StageType int stage, + @SplitScreen.StagePosition int position, @Nullable Bundle options) { switch (stage) { case STAGE_TYPE_UNDEFINED: { // Use the stage of the specified position is valid. @@ -216,7 +226,6 @@ public class SplitScreenController implements SplitScreen { return options; } - @Override public void dump(@NonNull PrintWriter pw, String prefix) { pw.println(prefix + TAG); if (mStageCoordinator != null) { @@ -224,4 +233,113 @@ public class SplitScreenController implements SplitScreen { } } + private class SplitScreenImpl implements SplitScreen { + @Override + public boolean isSplitScreenVisible() { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.isSplitScreenVisible(); + }, Boolean.class); + } + + @Override + public boolean moveToSideStage(int taskId, int sideStagePosition) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition); + }, Boolean.class); + } + + @Override + public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, + int sideStagePosition) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.moveToSideStage(task, sideStagePosition); + }, Boolean.class); + } + + @Override + public boolean removeFromSideStage(int taskId) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.removeFromSideStage(taskId); + }, Boolean.class); + } + + @Override + public void setSideStagePosition(int sideStagePosition) { + mMainExecutor.execute(() -> { + SplitScreenController.this.setSideStagePosition(sideStagePosition); + }); + } + + @Override + public void setSideStageVisibility(boolean visible) { + mMainExecutor.execute(() -> { + SplitScreenController.this.setSideStageVisibility(visible); + }); + } + + @Override + public void enterSplitScreen(int taskId, boolean leftOrTop) { + mMainExecutor.execute(() -> { + SplitScreenController.this.enterSplitScreen(taskId, leftOrTop); + }); + } + + @Override + public void exitSplitScreen() { + mMainExecutor.execute(() -> { + SplitScreenController.this.exitSplitScreen(); + }); + } + + @Override + public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { + try { + mMainExecutor.executeBlocking(() -> { + SplitScreenController.this.getStageBounds(outTopOrLeftBounds, + outBottomOrRightBounds); + }); + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to get stage bounds in 2s"); + } + } + + @Override + public void registerSplitScreenListener(SplitScreenListener listener) { + mMainExecutor.execute(() -> { + SplitScreenController.this.registerSplitScreenListener(listener); + }); + } + + @Override + public void unregisterSplitScreenListener(SplitScreenListener listener) { + mMainExecutor.execute(() -> { + SplitScreenController.this.unregisterSplitScreenListener(listener); + }); + } + + @Override + public void startTask(int taskId, int stage, int position, @Nullable Bundle options) { + mMainExecutor.execute(() -> { + SplitScreenController.this.startTask(taskId, stage, position, options); + }); + } + + @Override + public void startShortcut(String packageName, String shortcutId, int stage, int position, + @Nullable Bundle options, UserHandle user) { + mMainExecutor.execute(() -> { + SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position, + options, user); + }); + } + + @Override + public void startIntent(PendingIntent intent, int stage, int position, + @Nullable Bundle options) { + mMainExecutor.execute(() -> { + SplitScreenController.this.startIntent(intent, stage, position, options); + }); + } + } + } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 25721066b7130..19ecc49513e51 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -44,6 +44,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.app.ActivityManager; @@ -65,7 +66,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target; -import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreenController; import org.junit.Before; import org.junit.Test; @@ -92,7 +93,7 @@ public class DragAndDropPolicyTest { // Both the split-screen and start interface. @Mock - private SplitScreen mSplitScreenStarter; + private SplitScreenController mSplitScreenStarter; private DisplayLayout mLandscapeDisplayLayout; private DisplayLayout mPortraitDisplayLayout; @@ -127,8 +128,8 @@ public class DragAndDropPolicyTest { mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false); mInsets = Insets.of(0, 0, 0, 0); - mPolicy = new DragAndDropPolicy( - mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter); + mPolicy = spy(new DragAndDropPolicy( + mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter)); mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); setClipDataResizeable(mNonResizeableActivityClipData, false); @@ -204,8 +205,8 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @Test @@ -216,13 +217,13 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @Test @@ -233,13 +234,13 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @Test @@ -250,8 +251,8 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @Test @@ -262,8 +263,8 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @Test @@ -275,14 +276,14 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @Test @@ -294,14 +295,14 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); - verify(mSplitScreenStarter).startClipDescription(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT)); + verify(mSplitScreenStarter).startIntent(any(), + eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @Test diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 36506bf5715cc..b42dde63d1c2b 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -180,7 +180,7 @@ public abstract class WMShellBaseModule { DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, Optional legacySplitScreenOptional, - Optional splitScreenOptional, + Optional splitScreenOptional, Optional appPairsOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, @@ -205,7 +205,7 @@ public abstract class WMShellBaseModule { static Optional provideShellCommandHandler( ShellTaskOrganizer shellTaskOrganizer, Optional legacySplitScreenOptional, - Optional splitScreenOptional, + Optional splitScreenOptional, Optional pipOptional, Optional oneHandedOptional, Optional hideDisplayCutout, @@ -320,12 +320,21 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static Optional provideSplitScreen(ShellTaskOrganizer shellTaskOrganizer, + static Optional provideSplitScreen( + Optional splitScreenController) { + return splitScreenController.map((controller) -> controller.asSplitScreen()); + } + + @WMSingleton + @Provides + static Optional provideSplitScreenController( + ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + @ShellMainThread ShellExecutor mainExecutor) { if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) { return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context, - rootTaskDisplayAreaOrganizer)); + rootTaskDisplayAreaOrganizer, mainExecutor)); } else { return Optional.empty(); }