Loading libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +17 −1 Original line number Diff line number Diff line Loading @@ -103,6 +103,8 @@ public final class ShellCommandHandlerImpl { return runMoveToSideStage(args, pw); case "removeFromSideStage": return runRemoveFromSideStage(args, pw); case "setSideStageOutline": return runSetSideStageOutline(args, pw); case "setSideStagePosition": return runSetSideStagePosition(args, pw); case "setSideStageVisibility": Loading Loading @@ -161,6 +163,18 @@ public final class ShellCommandHandlerImpl { return true; } private boolean runSetSideStageOutline(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. pw.println("Error: whether to enable or disable side stage outline border should be" + " provided as arguments"); return false; } final boolean enable = new Boolean(args[2]); mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable)); return true; } private boolean runSetSideStagePosition(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. Loading @@ -175,7 +189,7 @@ public final class ShellCommandHandlerImpl { private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. pw.println("Error: side stage position should be provided as arguments"); pw.println("Error: side stage visibility should be provided as arguments"); return false; } final Boolean visible = new Boolean(args[2]); Loading @@ -197,6 +211,8 @@ public final class ShellCommandHandlerImpl { pw.println(" Move a task with given id in split-screen mode."); pw.println(" removeFromSideStage <taskId>"); pw.println(" Remove a task with given id in split-screen mode."); pw.println(" setSideStageOutline <true/false>"); pw.println(" Enable/Disable outline on the side-stage."); pw.println(" setSideStagePosition <SideStagePosition>"); pw.println(" Sets the position of the side-stage."); pw.println(" setSideStageVisibility <true/false>"); Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java +36 −38 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; Loading @@ -39,8 +38,6 @@ import android.view.WindowlessWindowManager; import com.android.wm.shell.R; import java.util.function.Supplier; /** * Handles drawing outline of the bounds of provided root surface. The outline will be drown with * the consideration of display insets like status bar, navigation bar and display cutout. Loading @@ -48,49 +45,27 @@ import java.util.function.Supplier; class OutlineManager extends WindowlessWindowManager { private static final String WINDOW_NAME = "SplitOutlineLayer"; private final Context mContext; private final int mOutlineColor; private final Rect mOutlineBounds = new Rect(); private final Rect mTmpBounds = new Rect(); private final Supplier<SurfaceControl> mOutlineSurfaceSupplier; private final SurfaceControlViewHost mViewHost; private final SurfaceControl mLeash; private SurfaceControlViewHost mViewHost; private SurfaceControl mHostLeash; private SurfaceControl mLeash; private int mOutlineColor; /** * Constructs {@link #OutlineManager} with indicated outline color for the provided root * surface. */ OutlineManager(Context context, Configuration configuration, Supplier<SurfaceControl> outlineSurfaceSupplier, int color) { OutlineManager(Context context, Configuration configuration) { super(configuration, null /* rootSurface */, null /* hostInputToken */); mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, null /* options */); mOutlineSurfaceSupplier = outlineSurfaceSupplier; mOutlineColor = color; mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) .inflate(R.layout.split_outline, null); rootView.updateOutlineBounds(mOutlineBounds, mOutlineColor); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.token = new Binder(); lp.setTitle(WINDOW_NAME); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports // TRUSTED_OVERLAY for windowless window without input channel. mViewHost.setView(rootView, lp); mLeash = getSurfaceControl(mViewHost.getWindowToken()); } @Override protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { b.setParent(mOutlineSurfaceSupplier.get()); b.setParent(mHostLeash); } boolean updateOutlineBounds(Rect rootBounds) { boolean drawOutlineBounds(Rect rootBounds) { if (mLeash == null || mViewHost == null) return false; computeOutlineBounds(mContext, rootBounds, mTmpBounds); if (mOutlineBounds.equals(mTmpBounds)) { return false; Loading @@ -107,9 +82,32 @@ class OutlineManager extends WindowlessWindowManager { return true; } @Nullable SurfaceControl getLeash() { return mLeash; void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) { if (mLeash != null || mViewHost != null) return; mHostLeash = hostLeash; mOutlineColor = color; mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) .inflate(R.layout.split_outline, null); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.token = new Binder(); lp.setTitle(WINDOW_NAME); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports // TRUSTED_OVERLAY for windowless window without input channel. mViewHost.setView(rootView, lp); mLeash = getSurfaceControl(mViewHost.getWindowToken()); t.setLayer(mLeash, Integer.MAX_VALUE); } void release() { if (mViewHost != null) { mViewHost.release(); } } private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +20 −28 Original line number Diff line number Diff line Loading @@ -21,12 +21,10 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; Loading @@ -48,20 +46,12 @@ class SideStage extends StageTaskListener { mContext = context; } @VisibleForTesting SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, SurfaceSession surfaceSession, OutlineManager outlineManager) { this(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); mOutlineManager = outlineManager; } void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, WindowContainerTransaction wct) { final WindowContainerToken rootToken = mRootTaskInfo.token; wct.setBounds(rootToken, rootBounds) .reparent(task.token, rootToken, true /* onTop*/) // Moving the root task to top after the child tasks were repareted , or the root // Moving the root task to top after the child tasks were reparented , or the root // task cannot be visible and focused. .reorder(rootToken, true /* onTop */); } Loading @@ -87,31 +77,33 @@ class SideStage extends StageTaskListener { return true; } @Override @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { super.onTaskAppeared(taskInfo, leash); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId && mOutlineManager == null) { mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration, () -> mRootLeash, Color.YELLOW); if (mOutlineManager.getLeash() != null) { mSyncQueue.runInSync(t -> { t.setLayer(mOutlineManager.getLeash(), Integer.MAX_VALUE); }); void enableOutline(boolean enable) { if (enable) { if (mOutlineManager == null && mRootTaskInfo != null) { mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration); mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW)); updateOutlineBounds(); } } else { if (mOutlineManager != null) { mOutlineManager.release(); mOutlineManager = null; } } } private void updateOutlineBounds() { if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return; mOutlineManager.drawOutlineBounds( mRootTaskInfo.configuration.windowConfiguration.getBounds()); } @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { super.onTaskInfoChanged(taskInfo); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId && mRootTaskInfo.isRunning) { mOutlineManager.updateOutlineBounds( mRootTaskInfo.configuration.windowConfiguration.getBounds()); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) { updateOutlineBounds(); } } } libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +4 −0 Original line number Diff line number Diff line Loading @@ -142,6 +142,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.removeFromSideStage(taskId); } public void setSideStageOutline(boolean enable) { mStageCoordinator.setSideStageOutline(enable); } public void setSideStagePosition(@SplitPosition int sideStagePosition) { mStageCoordinator.setSideStagePosition(sideStagePosition); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +4 −0 Original line number Diff line number Diff line Loading @@ -232,6 +232,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return result; } void setSideStageOutline(boolean enable) { mSideStage.enableOutline(enable); } /** Starts 2 tasks in one transition. */ void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +17 −1 Original line number Diff line number Diff line Loading @@ -103,6 +103,8 @@ public final class ShellCommandHandlerImpl { return runMoveToSideStage(args, pw); case "removeFromSideStage": return runRemoveFromSideStage(args, pw); case "setSideStageOutline": return runSetSideStageOutline(args, pw); case "setSideStagePosition": return runSetSideStagePosition(args, pw); case "setSideStageVisibility": Loading Loading @@ -161,6 +163,18 @@ public final class ShellCommandHandlerImpl { return true; } private boolean runSetSideStageOutline(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. pw.println("Error: whether to enable or disable side stage outline border should be" + " provided as arguments"); return false; } final boolean enable = new Boolean(args[2]); mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable)); return true; } private boolean runSetSideStagePosition(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. Loading @@ -175,7 +189,7 @@ public final class ShellCommandHandlerImpl { private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. pw.println("Error: side stage position should be provided as arguments"); pw.println("Error: side stage visibility should be provided as arguments"); return false; } final Boolean visible = new Boolean(args[2]); Loading @@ -197,6 +211,8 @@ public final class ShellCommandHandlerImpl { pw.println(" Move a task with given id in split-screen mode."); pw.println(" removeFromSideStage <taskId>"); pw.println(" Remove a task with given id in split-screen mode."); pw.println(" setSideStageOutline <true/false>"); pw.println(" Enable/Disable outline on the side-stage."); pw.println(" setSideStagePosition <SideStagePosition>"); pw.println(" Sets the position of the side-stage."); pw.println(" setSideStageVisibility <true/false>"); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java +36 −38 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; Loading @@ -39,8 +38,6 @@ import android.view.WindowlessWindowManager; import com.android.wm.shell.R; import java.util.function.Supplier; /** * Handles drawing outline of the bounds of provided root surface. The outline will be drown with * the consideration of display insets like status bar, navigation bar and display cutout. Loading @@ -48,49 +45,27 @@ import java.util.function.Supplier; class OutlineManager extends WindowlessWindowManager { private static final String WINDOW_NAME = "SplitOutlineLayer"; private final Context mContext; private final int mOutlineColor; private final Rect mOutlineBounds = new Rect(); private final Rect mTmpBounds = new Rect(); private final Supplier<SurfaceControl> mOutlineSurfaceSupplier; private final SurfaceControlViewHost mViewHost; private final SurfaceControl mLeash; private SurfaceControlViewHost mViewHost; private SurfaceControl mHostLeash; private SurfaceControl mLeash; private int mOutlineColor; /** * Constructs {@link #OutlineManager} with indicated outline color for the provided root * surface. */ OutlineManager(Context context, Configuration configuration, Supplier<SurfaceControl> outlineSurfaceSupplier, int color) { OutlineManager(Context context, Configuration configuration) { super(configuration, null /* rootSurface */, null /* hostInputToken */); mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, null /* options */); mOutlineSurfaceSupplier = outlineSurfaceSupplier; mOutlineColor = color; mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) .inflate(R.layout.split_outline, null); rootView.updateOutlineBounds(mOutlineBounds, mOutlineColor); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.token = new Binder(); lp.setTitle(WINDOW_NAME); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports // TRUSTED_OVERLAY for windowless window without input channel. mViewHost.setView(rootView, lp); mLeash = getSurfaceControl(mViewHost.getWindowToken()); } @Override protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { b.setParent(mOutlineSurfaceSupplier.get()); b.setParent(mHostLeash); } boolean updateOutlineBounds(Rect rootBounds) { boolean drawOutlineBounds(Rect rootBounds) { if (mLeash == null || mViewHost == null) return false; computeOutlineBounds(mContext, rootBounds, mTmpBounds); if (mOutlineBounds.equals(mTmpBounds)) { return false; Loading @@ -107,9 +82,32 @@ class OutlineManager extends WindowlessWindowManager { return true; } @Nullable SurfaceControl getLeash() { return mLeash; void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) { if (mLeash != null || mViewHost != null) return; mHostLeash = hostLeash; mOutlineColor = color; mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) .inflate(R.layout.split_outline, null); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.token = new Binder(); lp.setTitle(WINDOW_NAME); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports // TRUSTED_OVERLAY for windowless window without input channel. mViewHost.setView(rootView, lp); mLeash = getSurfaceControl(mViewHost.getWindowToken()); t.setLayer(mLeash, Integer.MAX_VALUE); } void release() { if (mViewHost != null) { mViewHost.release(); } } private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +20 −28 Original line number Diff line number Diff line Loading @@ -21,12 +21,10 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; Loading @@ -48,20 +46,12 @@ class SideStage extends StageTaskListener { mContext = context; } @VisibleForTesting SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, SurfaceSession surfaceSession, OutlineManager outlineManager) { this(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); mOutlineManager = outlineManager; } void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, WindowContainerTransaction wct) { final WindowContainerToken rootToken = mRootTaskInfo.token; wct.setBounds(rootToken, rootBounds) .reparent(task.token, rootToken, true /* onTop*/) // Moving the root task to top after the child tasks were repareted , or the root // Moving the root task to top after the child tasks were reparented , or the root // task cannot be visible and focused. .reorder(rootToken, true /* onTop */); } Loading @@ -87,31 +77,33 @@ class SideStage extends StageTaskListener { return true; } @Override @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { super.onTaskAppeared(taskInfo, leash); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId && mOutlineManager == null) { mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration, () -> mRootLeash, Color.YELLOW); if (mOutlineManager.getLeash() != null) { mSyncQueue.runInSync(t -> { t.setLayer(mOutlineManager.getLeash(), Integer.MAX_VALUE); }); void enableOutline(boolean enable) { if (enable) { if (mOutlineManager == null && mRootTaskInfo != null) { mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration); mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW)); updateOutlineBounds(); } } else { if (mOutlineManager != null) { mOutlineManager.release(); mOutlineManager = null; } } } private void updateOutlineBounds() { if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return; mOutlineManager.drawOutlineBounds( mRootTaskInfo.configuration.windowConfiguration.getBounds()); } @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { super.onTaskInfoChanged(taskInfo); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId && mRootTaskInfo.isRunning) { mOutlineManager.updateOutlineBounds( mRootTaskInfo.configuration.windowConfiguration.getBounds()); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) { updateOutlineBounds(); } } }
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +4 −0 Original line number Diff line number Diff line Loading @@ -142,6 +142,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.removeFromSideStage(taskId); } public void setSideStageOutline(boolean enable) { mStageCoordinator.setSideStageOutline(enable); } public void setSideStagePosition(@SplitPosition int sideStagePosition) { mStageCoordinator.setSideStagePosition(sideStagePosition); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +4 −0 Original line number Diff line number Diff line Loading @@ -232,6 +232,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return result; } void setSideStageOutline(boolean enable) { mSideStage.enableOutline(enable); } /** Starts 2 tasks in one transition. */ void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, Loading