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);
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>");

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.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) {

View File

@@ -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();
}
}
}

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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,