Add adb command to toggle side stage outline border
Make the outline boarder of the side stage default off. Add an adb command to toggle on/off the boarder. $adb shell dumpsys activity service SystemUIService WMShell setSideStateOutline <true/false> Bug: 189839391 Test: verified the adb command could toggle on/off side stage outline border. Change-Id: I4c09b072b384badd206f0e81eca6daa033ac8565
This commit is contained in:
@@ -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":
|
||||
@@ -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.
|
||||
@@ -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]);
|
||||
@@ -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>");
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -53,7 +53,6 @@ public class SideStageTests extends ShellTestCase {
|
||||
@Mock private SyncTransactionQueue mSyncQueue;
|
||||
@Mock private ActivityManager.RunningTaskInfo mRootTask;
|
||||
@Mock private SurfaceControl mRootLeash;
|
||||
@Mock private OutlineManager mOutlineManager;
|
||||
@Spy private WindowContainerTransaction mWct;
|
||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private SideStage mSideStage;
|
||||
@@ -63,7 +62,7 @@ public class SideStageTests extends ShellTestCase {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
|
||||
mSyncQueue, mSurfaceSession, mOutlineManager);
|
||||
mSyncQueue, mSurfaceSession);
|
||||
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
@Mock private TransactionPool mTransactionPool;
|
||||
@Mock private Transitions mTransitions;
|
||||
@Mock private SurfaceSession mSurfaceSession;
|
||||
@Mock private OutlineManager mOutlineManager;
|
||||
private SplitLayout mSplitLayout;
|
||||
private MainStage mMainStage;
|
||||
private SideStage mSideStage;
|
||||
@@ -104,8 +103,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
|
||||
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||
mOutlineManager);
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
|
||||
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
|
||||
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
|
||||
|
||||
Reference in New Issue
Block a user