Merge "Add adb command to toggle side stage outline border" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-07-14 04:03:59 +00:00
committed by Android (Google) Code Review
7 changed files with 82 additions and 71 deletions

View File

@@ -103,6 +103,8 @@ public final class ShellCommandHandlerImpl {
return runMoveToSideStage(args, pw); return runMoveToSideStage(args, pw);
case "removeFromSideStage": case "removeFromSideStage":
return runRemoveFromSideStage(args, pw); return runRemoveFromSideStage(args, pw);
case "setSideStageOutline":
return runSetSideStageOutline(args, pw);
case "setSideStagePosition": case "setSideStagePosition":
return runSetSideStagePosition(args, pw); return runSetSideStagePosition(args, pw);
case "setSideStageVisibility": case "setSideStageVisibility":
@@ -161,6 +163,18 @@ public final class ShellCommandHandlerImpl {
return true; 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) { private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
if (args.length < 3) { if (args.length < 3) {
// First arguments are "WMShell" and command name. // First arguments are "WMShell" and command name.
@@ -175,7 +189,7 @@ public final class ShellCommandHandlerImpl {
private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) { private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) {
if (args.length < 3) { if (args.length < 3) {
// First arguments are "WMShell" and command name. // 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; return false;
} }
final Boolean visible = new Boolean(args[2]); 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(" Move a task with given id in split-screen mode.");
pw.println(" removeFromSideStage <taskId>"); pw.println(" removeFromSideStage <taskId>");
pw.println(" Remove a task with given id in split-screen mode."); 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(" setSideStagePosition <SideStagePosition>");
pw.println(" Sets the position of the side-stage."); pw.println(" Sets the position of the side-stage.");
pw.println(" setSideStageVisibility <true/false>"); pw.println(" setSideStageVisibility <true/false>");

View File

@@ -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.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
@@ -39,8 +38,6 @@ import android.view.WindowlessWindowManager;
import com.android.wm.shell.R; 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 * 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. * 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 { class OutlineManager extends WindowlessWindowManager {
private static final String WINDOW_NAME = "SplitOutlineLayer"; private static final String WINDOW_NAME = "SplitOutlineLayer";
private final Context mContext; private final Context mContext;
private final int mOutlineColor;
private final Rect mOutlineBounds = new Rect(); private final Rect mOutlineBounds = new Rect();
private final Rect mTmpBounds = new Rect(); private final Rect mTmpBounds = new Rect();
private final Supplier<SurfaceControl> mOutlineSurfaceSupplier; private SurfaceControlViewHost mViewHost;
private final SurfaceControlViewHost mViewHost; private SurfaceControl mHostLeash;
private final SurfaceControl mLeash; private SurfaceControl mLeash;
private int mOutlineColor;
/** OutlineManager(Context context, Configuration configuration) {
* Constructs {@link #OutlineManager} with indicated outline color for the provided root
* surface.
*/
OutlineManager(Context context, Configuration configuration,
Supplier<SurfaceControl> outlineSurfaceSupplier, int color) {
super(configuration, null /* rootSurface */, null /* hostInputToken */); super(configuration, null /* rootSurface */, null /* hostInputToken */);
mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
null /* options */); 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 @Override
protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { 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); computeOutlineBounds(mContext, rootBounds, mTmpBounds);
if (mOutlineBounds.equals(mTmpBounds)) { if (mOutlineBounds.equals(mTmpBounds)) {
return false; return false;
@@ -107,9 +82,32 @@ class OutlineManager extends WindowlessWindowManager {
return true; return true;
} }
@Nullable void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) {
SurfaceControl getLeash() { if (mLeash != null || mViewHost != null) return;
return mLeash;
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) { private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) {

View File

@@ -21,12 +21,10 @@ import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Rect; import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.SurfaceSession; import android.view.SurfaceSession;
import android.window.WindowContainerToken; import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue;
@@ -48,20 +46,12 @@ class SideStage extends StageTaskListener {
mContext = context; 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, void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
WindowContainerTransaction wct) { WindowContainerTransaction wct) {
final WindowContainerToken rootToken = mRootTaskInfo.token; final WindowContainerToken rootToken = mRootTaskInfo.token;
wct.setBounds(rootToken, rootBounds) wct.setBounds(rootToken, rootBounds)
.reparent(task.token, rootToken, true /* onTop*/) .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. // task cannot be visible and focused.
.reorder(rootToken, true /* onTop */); .reorder(rootToken, true /* onTop */);
} }
@@ -87,31 +77,33 @@ class SideStage extends StageTaskListener {
return true; return true;
} }
@Override void enableOutline(boolean enable) {
@CallSuper if (enable) {
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (mOutlineManager == null && mRootTaskInfo != null) {
super.onTaskAppeared(taskInfo, leash); mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration);
if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW));
&& mOutlineManager == null) { updateOutlineBounds();
mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration, }
() -> mRootLeash, } else {
Color.YELLOW); if (mOutlineManager != null) {
if (mOutlineManager.getLeash() != null) { mOutlineManager.release();
mSyncQueue.runInSync(t -> { mOutlineManager = null;
t.setLayer(mOutlineManager.getLeash(), Integer.MAX_VALUE);
});
} }
} }
} }
private void updateOutlineBounds() {
if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return;
mOutlineManager.drawOutlineBounds(
mRootTaskInfo.configuration.windowConfiguration.getBounds());
}
@Override @Override
@CallSuper @CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
super.onTaskInfoChanged(taskInfo); super.onTaskInfoChanged(taskInfo);
if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) {
&& mRootTaskInfo.isRunning) { updateOutlineBounds();
mOutlineManager.updateOutlineBounds(
mRootTaskInfo.configuration.windowConfiguration.getBounds());
} }
} }
} }

View File

@@ -142,6 +142,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator.removeFromSideStage(taskId); return mStageCoordinator.removeFromSideStage(taskId);
} }
public void setSideStageOutline(boolean enable) {
mStageCoordinator.setSideStageOutline(enable);
}
public void setSideStagePosition(@SplitPosition int sideStagePosition) { public void setSideStagePosition(@SplitPosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition); mStageCoordinator.setSideStagePosition(sideStagePosition);
} }

View File

@@ -232,6 +232,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return result; return result;
} }
void setSideStageOutline(boolean enable) {
mSideStage.enableOutline(enable);
}
/** Starts 2 tasks in one transition. */ /** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,

View File

@@ -53,7 +53,6 @@ public class SideStageTests extends ShellTestCase {
@Mock private SyncTransactionQueue mSyncQueue; @Mock private SyncTransactionQueue mSyncQueue;
@Mock private ActivityManager.RunningTaskInfo mRootTask; @Mock private ActivityManager.RunningTaskInfo mRootTask;
@Mock private SurfaceControl mRootLeash; @Mock private SurfaceControl mRootLeash;
@Mock private OutlineManager mOutlineManager;
@Spy private WindowContainerTransaction mWct; @Spy private WindowContainerTransaction mWct;
private SurfaceSession mSurfaceSession = new SurfaceSession(); private SurfaceSession mSurfaceSession = new SurfaceSession();
private SideStage mSideStage; private SideStage mSideStage;
@@ -63,7 +62,7 @@ public class SideStageTests extends ShellTestCase {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
mSyncQueue, mSurfaceSession, mOutlineManager); mSyncQueue, mSurfaceSession);
mSideStage.onTaskAppeared(mRootTask, mRootLeash); mSideStage.onTaskAppeared(mRootTask, mRootLeash);
} }

View File

@@ -82,7 +82,6 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private TransactionPool mTransactionPool; @Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions; @Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession; @Mock private SurfaceSession mSurfaceSession;
@Mock private OutlineManager mOutlineManager;
private SplitLayout mSplitLayout; private SplitLayout mSplitLayout;
private MainStage mMainStage; private MainStage mMainStage;
private SideStage mSideStage; private SideStage mSideStage;
@@ -104,8 +103,7 @@ public class SplitTransitionTests extends ShellTestCase {
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mOutlineManager);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,