Consolidate the behavior and style of side stage outline
Update the style of side stage outline to match the current design. And make sure the outline will interact properly with task bar expanding, collapsing, swipe up, drag to split, recents shortcut to split and drag divider bar to resize gestures. Bug: 200850654 Test: atest WMShellUnitTests Test: enable side stage outline, observed it behaves properly. Change-Id: I3f0795adc34a0efb19d35b78d54364da4189a043
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.wm.shell.splitscreen.OutlineRoot
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@@ -23,4 +23,4 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent" />
|
||||
|
||||
</com.android.wm.shell.splitscreen.OutlineRoot>
|
||||
</FrameLayout>
|
||||
|
||||
@@ -22,19 +22,23 @@ 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;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.view.IWindow;
|
||||
import android.view.InsetsSource;
|
||||
import android.view.InsetsState;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
import android.view.WindowlessWindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
@@ -45,17 +49,22 @@ import com.android.wm.shell.R;
|
||||
class OutlineManager extends WindowlessWindowManager {
|
||||
private static final String WINDOW_NAME = "SplitOutlineLayer";
|
||||
private final Context mContext;
|
||||
private final Rect mOutlineBounds = new Rect();
|
||||
private final Rect mTmpBounds = new Rect();
|
||||
private final Rect mRootBounds = new Rect();
|
||||
private final Rect mTempRect = new Rect();
|
||||
private final Rect mLastOutlineBounds = new Rect();
|
||||
private final InsetsState mInsetsState = new InsetsState();
|
||||
private final int mExpandedTaskBarHeight;
|
||||
private OutlineView mOutlineView;
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
private SurfaceControl mHostLeash;
|
||||
private SurfaceControl mLeash;
|
||||
private int mOutlineColor;
|
||||
|
||||
OutlineManager(Context context, Configuration configuration) {
|
||||
super(configuration, null /* rootSurface */, null /* hostInputToken */);
|
||||
mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
|
||||
null /* options */);
|
||||
mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.taskbar_frame_height);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,65 +72,110 @@ class OutlineManager extends WindowlessWindowManager {
|
||||
b.setParent(mHostLeash);
|
||||
}
|
||||
|
||||
boolean drawOutlineBounds(Rect rootBounds) {
|
||||
if (mLeash == null || mViewHost == null) return false;
|
||||
|
||||
computeOutlineBounds(mContext, rootBounds, mTmpBounds);
|
||||
if (mOutlineBounds.equals(mTmpBounds)) {
|
||||
return false;
|
||||
}
|
||||
mOutlineBounds.set(mTmpBounds);
|
||||
|
||||
((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor);
|
||||
final WindowManager.LayoutParams lp =
|
||||
(WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
|
||||
lp.width = rootBounds.width();
|
||||
lp.height = rootBounds.height();
|
||||
mViewHost.relayout(lp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) {
|
||||
void inflate(SurfaceControl rootLeash, Rect rootBounds) {
|
||||
if (mLeash != null || mViewHost != null) return;
|
||||
|
||||
mHostLeash = hostLeash;
|
||||
mOutlineColor = color;
|
||||
mHostLeash = rootLeash;
|
||||
mRootBounds.set(rootBounds);
|
||||
mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
|
||||
final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext)
|
||||
|
||||
final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.split_outline, null);
|
||||
mOutlineView = rootLayout.findViewById(R.id.split_outline);
|
||||
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
|
||||
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
|
||||
lp.width = mRootBounds.width();
|
||||
lp.height = mRootBounds.height();
|
||||
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);
|
||||
mViewHost.setView(rootLayout, lp);
|
||||
mLeash = getSurfaceControl(mViewHost.getWindowToken());
|
||||
t.setLayer(mLeash, Integer.MAX_VALUE);
|
||||
|
||||
drawOutline();
|
||||
}
|
||||
|
||||
void release() {
|
||||
if (mViewHost != null) {
|
||||
mViewHost.release();
|
||||
mViewHost = null;
|
||||
}
|
||||
mRootBounds.setEmpty();
|
||||
mLastOutlineBounds.setEmpty();
|
||||
mOutlineView = null;
|
||||
mHostLeash = null;
|
||||
mLeash = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SurfaceControl getOutlineLeash() {
|
||||
return mLeash;
|
||||
}
|
||||
|
||||
void setVisibility(boolean visible) {
|
||||
if (mOutlineView != null) {
|
||||
mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) {
|
||||
computeDisplayStableBounds(context, outBounds);
|
||||
outBounds.intersect(rootBounds);
|
||||
void setRootBounds(Rect rootBounds) {
|
||||
if (mViewHost == null || mViewHost.getView() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mRootBounds.equals(rootBounds)) {
|
||||
WindowManager.LayoutParams lp =
|
||||
(WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
|
||||
lp.width = rootBounds.width();
|
||||
lp.height = rootBounds.height();
|
||||
mViewHost.relayout(lp);
|
||||
mRootBounds.set(rootBounds);
|
||||
drawOutline();
|
||||
}
|
||||
}
|
||||
|
||||
void onInsetsChanged(InsetsState insetsState) {
|
||||
if (!mInsetsState.equals(insetsState)) {
|
||||
mInsetsState.set(insetsState);
|
||||
drawOutline();
|
||||
}
|
||||
}
|
||||
|
||||
private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) {
|
||||
outBounds.set(rootBounds);
|
||||
final InsetsSource taskBarInsetsSource =
|
||||
insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
|
||||
// Only insets the divider bar with task bar when it's expanded so that the rounded corners
|
||||
// will be drawn against task bar.
|
||||
if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
|
||||
outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds));
|
||||
}
|
||||
|
||||
// Offset the coordinate from screen based to surface based.
|
||||
outBounds.offset(-rootBounds.left, -rootBounds.top);
|
||||
}
|
||||
|
||||
private static void computeDisplayStableBounds(Context context, Rect outBounds) {
|
||||
final WindowMetrics windowMetrics =
|
||||
context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
|
||||
outBounds.set(windowMetrics.getBounds());
|
||||
outBounds.inset(windowMetrics.getWindowInsets().getInsets(
|
||||
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()));
|
||||
void drawOutline() {
|
||||
if (mOutlineView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
computeOutlineBounds(mRootBounds, mInsetsState, mTempRect);
|
||||
if (mTempRect.equals(mLastOutlineBounds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewGroup.MarginLayoutParams lp =
|
||||
(ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams();
|
||||
lp.leftMargin = mTempRect.left;
|
||||
lp.topMargin = mTempRect.top;
|
||||
lp.width = mTempRect.width();
|
||||
lp.height = mTempRect.height();
|
||||
mOutlineView.setLayoutParams(lp);
|
||||
mLastOutlineBounds.set(mTempRect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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.splitscreen;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
/** Root layout for holding split outline. */
|
||||
public class OutlineRoot extends FrameLayout {
|
||||
public OutlineRoot(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public OutlineRoot(@NonNull Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
private OutlineView mOutlineView;
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mOutlineView = findViewById(R.id.split_outline);
|
||||
}
|
||||
|
||||
void updateOutlineBounds(Rect bounds, int color) {
|
||||
mOutlineView.updateOutlineBounds(bounds, color);
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,17 @@
|
||||
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.RoundedCorner;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -33,44 +37,46 @@ import com.android.internal.R;
|
||||
/** View for drawing split outline. */
|
||||
public class OutlineView extends View {
|
||||
private final Paint mPaint = new Paint();
|
||||
private final Rect mBounds = new Rect();
|
||||
private final Path mPath = new Path();
|
||||
private final float[] mRadii = new float[8];
|
||||
|
||||
public OutlineView(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public OutlineView(@NonNull Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeWidth(
|
||||
getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
|
||||
mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeWidth(getResources()
|
||||
.getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
|
||||
protected void onAttachedToWindow() {
|
||||
// TODO(b/200850654): match the screen corners with the actual display decor.
|
||||
mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT);
|
||||
mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT);
|
||||
mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT);
|
||||
mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT);
|
||||
}
|
||||
|
||||
void updateOutlineBounds(Rect bounds, int color) {
|
||||
if (mBounds.equals(bounds) && mPaint.getColor() == color) return;
|
||||
mBounds.set(bounds);
|
||||
mPaint.setColor(color);
|
||||
private int getCornerRadius(@RoundedCorner.Position int position) {
|
||||
final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position);
|
||||
return roundedCorner == null ? 0 : roundedCorner.getRadius();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
if (changed) {
|
||||
mPath.reset();
|
||||
mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mBounds.isEmpty()) return;
|
||||
final Path path = new Region(mBounds).getBoundaryPath();
|
||||
canvas.drawPath(path, mPaint);
|
||||
canvas.drawPath(mPath, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,19 @@
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayInsetsController;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
/**
|
||||
@@ -34,7 +38,8 @@ import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
*
|
||||
* @see StageCoordinator
|
||||
*/
|
||||
class SideStage extends StageTaskListener {
|
||||
class SideStage extends StageTaskListener implements
|
||||
DisplayInsetsController.OnInsetsChangedListener {
|
||||
private static final String TAG = SideStage.class.getSimpleName();
|
||||
private final Context mContext;
|
||||
private OutlineManager mOutlineManager;
|
||||
@@ -77,33 +82,61 @@ class SideStage extends StageTaskListener {
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@Nullable
|
||||
public SurfaceControl getOutlineLeash() {
|
||||
return mOutlineManager.getOutlineLeash();
|
||||
}
|
||||
|
||||
private void updateOutlineBounds() {
|
||||
if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return;
|
||||
mOutlineManager.drawOutlineBounds(
|
||||
mRootTaskInfo.configuration.windowConfiguration.getBounds());
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
super.onTaskAppeared(taskInfo, leash);
|
||||
if (isRootTask(taskInfo)) {
|
||||
mOutlineManager = new OutlineManager(mContext, taskInfo.configuration);
|
||||
enableOutline(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
super.onTaskInfoChanged(taskInfo);
|
||||
if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) {
|
||||
updateOutlineBounds();
|
||||
if (isRootTask(taskInfo)) {
|
||||
mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId;
|
||||
}
|
||||
|
||||
void enableOutline(boolean enable) {
|
||||
if (mOutlineManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
if (mRootTaskInfo != null) {
|
||||
mOutlineManager.inflate(mRootLeash,
|
||||
mRootTaskInfo.configuration.windowConfiguration.getBounds());
|
||||
}
|
||||
} else {
|
||||
mOutlineManager.release();
|
||||
}
|
||||
}
|
||||
|
||||
void setOutlineVisibility(boolean visible) {
|
||||
mOutlineManager.setVisibility(visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsChanged(InsetsState insetsState) {
|
||||
mOutlineManager.onInsetsChanged(insetsState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsControlChanged(InsetsState insetsState,
|
||||
InsetsSourceControl[] activeControls) {
|
||||
insetsChanged(insetsState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,11 +257,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
}
|
||||
|
||||
final RemoteAnimationTarget divider = mStageCoordinator.getDividerBarLegacyTarget();
|
||||
if (divider.leash != null) {
|
||||
t.show(divider.leash);
|
||||
}
|
||||
|
||||
t.apply();
|
||||
if (finishedCallback != null) {
|
||||
try {
|
||||
@@ -300,7 +295,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
transaction.apply();
|
||||
transaction.close();
|
||||
return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
|
||||
return new RemoteAnimationTarget[]{
|
||||
mStageCoordinator.getDividerBarLegacyTarget(),
|
||||
mStageCoordinator.getOutlineLegacyTarget()};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -201,6 +201,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSurfaceSession);
|
||||
mDisplayImeController = displayImeController;
|
||||
mDisplayInsetsController = displayInsetsController;
|
||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
|
||||
mRootTDAOrganizer.registerListener(displayId, this);
|
||||
final DeviceStateManager deviceStateManager =
|
||||
mContext.getSystemService(DeviceStateManager.class);
|
||||
@@ -682,6 +683,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
|
||||
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
|
||||
applyDividerVisibility(t);
|
||||
applyOutlineVisibility(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -703,6 +705,19 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
}
|
||||
}
|
||||
|
||||
private void applyOutlineVisibility(SurfaceControl.Transaction t) {
|
||||
final SurfaceControl outlineLeash = mSideStage.getOutlineLeash();
|
||||
if (outlineLeash == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDividerVisible) {
|
||||
t.show(outlineLeash).setLayer(outlineLeash, SPLIT_DIVIDER_LAYER);
|
||||
} else {
|
||||
t.hide(outlineLeash);
|
||||
}
|
||||
}
|
||||
|
||||
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
|
||||
final boolean hasChildren = stageListener.mHasChildren;
|
||||
final boolean isSideStage = stageListener == mSideStageListener;
|
||||
@@ -762,6 +777,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
@Override
|
||||
public void onLayoutChanging(SplitLayout layout) {
|
||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
||||
mSideStage.setOutlineVisibility(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -770,6 +786,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
updateWindowBounds(layout, wct);
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
||||
mSideStage.setOutlineVisibility(true);
|
||||
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
|
||||
}
|
||||
|
||||
@@ -1145,6 +1162,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
|
||||
}
|
||||
|
||||
RemoteAnimationTarget getOutlineLegacyTarget() {
|
||||
final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds();
|
||||
// Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to
|
||||
// distinguish as a split auxiliary target in Launcher.
|
||||
return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
|
||||
mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */,
|
||||
null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
|
||||
new android.graphics.Point(0, 0) /* position */, bounds, bounds,
|
||||
new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
|
||||
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||
final String innerPrefix = prefix + " ";
|
||||
|
||||
@@ -138,8 +138,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
mRootTaskInfo = taskInfo;
|
||||
mCallbacks.onRootTaskAppeared();
|
||||
sendStatusChanged();
|
||||
mSyncQueue.runInSync(t -> mDimLayer =
|
||||
SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
|
||||
mSyncQueue.runInSync(t -> {
|
||||
t.hide(mRootLeash);
|
||||
mDimLayer =
|
||||
SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession);
|
||||
});
|
||||
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
||||
final int taskId = taskInfo.taskId;
|
||||
mChildrenLeashes.put(taskId, leash);
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -58,6 +59,7 @@ public class SideStageTests extends ShellTestCase {
|
||||
private SideStage mSideStage;
|
||||
|
||||
@Before
|
||||
@UiThreadTest
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
@@ -96,6 +97,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
private ActivityManager.RunningTaskInfo mSideChild;
|
||||
|
||||
@Before
|
||||
@UiThreadTest
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
final ShellExecutor mockExecutor = mock(ShellExecutor.class);
|
||||
|
||||
@@ -62,6 +62,7 @@ public final class StageTaskListenerTests {
|
||||
@Mock private SyncTransactionQueue mSyncQueue;
|
||||
@Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
|
||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private SurfaceControl mSurfaceControl;
|
||||
private ActivityManager.RunningTaskInfo mRootTask;
|
||||
private StageTaskListener mStageTaskListener;
|
||||
|
||||
@@ -76,7 +77,8 @@ public final class StageTaskListenerTests {
|
||||
mSurfaceSession);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
mRootTask.parentTaskId = INVALID_TASK_ID;
|
||||
mStageTaskListener.onTaskAppeared(mRootTask, new SurfaceControl());
|
||||
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
|
||||
mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,7 +105,7 @@ public final class StageTaskListenerTests {
|
||||
final ActivityManager.RunningTaskInfo childTask =
|
||||
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
|
||||
|
||||
mStageTaskListener.onTaskAppeared(childTask, new SurfaceControl());
|
||||
mStageTaskListener.onTaskAppeared(childTask, mSurfaceControl);
|
||||
|
||||
assertThat(mStageTaskListener.mChildrenTaskInfo.contains(childTask.taskId)).isTrue();
|
||||
verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
|
||||
|
||||
Reference in New Issue
Block a user