Update split resizing transition to match design
Remove the support of drawing side stage outline and update OutlineManager to SplitDecorManager to help rendering decor view for split screen like providing resizing hint. Fix: 202740435 Fix: 202174975 Bug: 201653912 Bug: 200850654 Test: atest WMShellUnitTests Test: manual check split resizing behavior Merged-In: I4033255d8e2a94f69667ca1365b17da956816cde Change-Id: I4033255d8e2a94f69667ca1365b17da956816cde
This commit is contained in:
30
libs/WindowManager/Shell/res/layout/split_decor.xml
Normal file
30
libs/WindowManager/Shell/res/layout/split_decor.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
<ImageView android:id="@+id/split_resizing_icon"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="0dp"
|
||||
android:visibility="gone"
|
||||
android:background="@null"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,18 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -103,8 +103,6 @@ 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":
|
||||
@@ -163,18 +161,6 @@ 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.
|
||||
|
||||
@@ -131,7 +131,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
|
||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
|
||||
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
|
||||
mRootTaskInfo.configuration, this /* layoutChangeListener */,
|
||||
mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
|
||||
mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
|
||||
true /* applyDismissingParallax */);
|
||||
mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
|
||||
|
||||
final WindowContainerToken token1 = task1.token;
|
||||
|
||||
@@ -23,16 +23,22 @@ import android.view.SurfaceSession;
|
||||
* Helpers for handling surface.
|
||||
*/
|
||||
public class SurfaceUtils {
|
||||
/** Creates a dim layer above indicated host surface. */
|
||||
/** Creates a dim layer above host surface. */
|
||||
public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host,
|
||||
String name, SurfaceSession surfaceSession) {
|
||||
SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession)
|
||||
.setParent(host)
|
||||
.setColorLayer()
|
||||
.setName(name)
|
||||
.setCallsite("SurfaceUtils.makeDimLayer")
|
||||
.build();
|
||||
final SurfaceControl dimLayer = makeColorLayer(host, name, surfaceSession);
|
||||
t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
|
||||
return dimLayer;
|
||||
}
|
||||
|
||||
/** Creates a color layer for host surface. */
|
||||
public static SurfaceControl makeColorLayer(SurfaceControl host, String name,
|
||||
SurfaceSession surfaceSession) {
|
||||
return new SurfaceControl.Builder(surfaceSession)
|
||||
.setParent(host)
|
||||
.setColorLayer()
|
||||
.setName(name)
|
||||
.setCallsite("SurfaceUtils.makeColorLayer")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.common.split;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Binder;
|
||||
import android.view.IWindow;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowlessWindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.common.SurfaceUtils;
|
||||
|
||||
/**
|
||||
* Handles split decor like showing resizing hint for a specific split.
|
||||
*/
|
||||
public class SplitDecorManager extends WindowlessWindowManager {
|
||||
private static final String TAG = SplitDecorManager.class.getSimpleName();
|
||||
private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
|
||||
|
||||
private final IconProvider mIconProvider;
|
||||
private final SurfaceSession mSurfaceSession;
|
||||
|
||||
private Drawable mIcon;
|
||||
private ImageView mResizingIconView;
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
private SurfaceControl mHostLeash;
|
||||
private SurfaceControl mIconLeash;
|
||||
private SurfaceControl mBackgroundLeash;
|
||||
|
||||
public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
|
||||
SurfaceSession surfaceSession) {
|
||||
super(configuration, null /* rootSurface */, null /* hostInputToken */);
|
||||
mIconProvider = iconProvider;
|
||||
mSurfaceSession = surfaceSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
|
||||
// Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
|
||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
|
||||
.setContainerLayer()
|
||||
.setName(TAG)
|
||||
.setHidden(true)
|
||||
.setParent(mHostLeash)
|
||||
.setCallsite("SplitDecorManager#attachToParentSurface");
|
||||
mIconLeash = builder.build();
|
||||
b.setParent(mIconLeash);
|
||||
}
|
||||
|
||||
/** Inflates split decor surface on the root surface. */
|
||||
public void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) {
|
||||
if (mIconLeash != null && mViewHost != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
|
||||
null /* options */);
|
||||
mHostLeash = rootLeash;
|
||||
mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this);
|
||||
|
||||
final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
|
||||
.inflate(R.layout.split_decor, null);
|
||||
mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon);
|
||||
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
|
||||
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
|
||||
lp.width = rootBounds.width();
|
||||
lp.height = rootBounds.height();
|
||||
lp.token = new Binder();
|
||||
lp.setTitle(TAG);
|
||||
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(rootLayout, lp);
|
||||
}
|
||||
|
||||
/** Releases the surfaces for split decor. */
|
||||
public void release(SurfaceControl.Transaction t) {
|
||||
if (mViewHost != null) {
|
||||
mViewHost.release();
|
||||
mViewHost = null;
|
||||
}
|
||||
if (mIconLeash != null) {
|
||||
t.remove(mIconLeash);
|
||||
mIconLeash = null;
|
||||
}
|
||||
if (mBackgroundLeash != null) {
|
||||
t.remove(mBackgroundLeash);
|
||||
mBackgroundLeash = null;
|
||||
}
|
||||
mHostLeash = null;
|
||||
mIcon = null;
|
||||
mResizingIconView = null;
|
||||
}
|
||||
|
||||
/** Showing resizing hint. */
|
||||
public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
|
||||
SurfaceControl.Transaction t) {
|
||||
if (mResizingIconView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIcon == null) {
|
||||
// TODO: add fade-in animation.
|
||||
mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
|
||||
RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
|
||||
t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
|
||||
.setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
|
||||
.show(mBackgroundLeash);
|
||||
|
||||
mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
|
||||
mResizingIconView.setImageDrawable(mIcon);
|
||||
mResizingIconView.setVisibility(View.VISIBLE);
|
||||
|
||||
WindowManager.LayoutParams lp =
|
||||
(WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
|
||||
lp.width = mIcon.getIntrinsicWidth();
|
||||
lp.height = mIcon.getIntrinsicHeight();
|
||||
mViewHost.relayout(lp);
|
||||
t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
|
||||
}
|
||||
|
||||
t.setPosition(mIconLeash,
|
||||
newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2,
|
||||
newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2);
|
||||
}
|
||||
|
||||
/** Stops showing resizing hint. */
|
||||
public void onResized(Rect newBounds, SurfaceControl.Transaction t) {
|
||||
if (mResizingIconView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIcon != null) {
|
||||
mResizingIconView.setVisibility(View.GONE);
|
||||
mResizingIconView.setImageDrawable(null);
|
||||
t.remove(mBackgroundLeash).hide(mIconLeash);
|
||||
mIcon = null;
|
||||
mBackgroundLeash = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
|
||||
return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
private final SplitWindowManager mSplitWindowManager;
|
||||
private final DisplayImeController mDisplayImeController;
|
||||
private final ImePositionProcessor mImePositionProcessor;
|
||||
private final DismissingParallaxPolicy mDismissingParallaxPolicy;
|
||||
private final DismissingEffectPolicy mDismissingEffectPolicy;
|
||||
private final ShellTaskOrganizer mTaskOrganizer;
|
||||
private final InsetsState mInsetsState = new InsetsState();
|
||||
|
||||
@@ -119,7 +119,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
public SplitLayout(String windowName, Context context, Configuration configuration,
|
||||
SplitLayoutHandler splitLayoutHandler,
|
||||
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
|
||||
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
|
||||
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer,
|
||||
boolean applyDismissingParallax) {
|
||||
mContext = context.createConfigurationContext(configuration);
|
||||
mOrientation = configuration.orientation;
|
||||
mRotation = configuration.windowConfiguration.getRotation();
|
||||
@@ -129,7 +130,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
parentContainerCallbacks);
|
||||
mTaskOrganizer = taskOrganizer;
|
||||
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
|
||||
mDismissingParallaxPolicy = new DismissingParallaxPolicy();
|
||||
mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax);
|
||||
|
||||
final Resources resources = context.getResources();
|
||||
mDividerWindowWidth = resources.getDimensionPixelSize(
|
||||
@@ -247,7 +248,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
}
|
||||
DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
|
||||
DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
|
||||
mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape);
|
||||
mDismissingEffectPolicy.applyDividerPosition(position, isLandscape);
|
||||
}
|
||||
|
||||
/** Inflates {@link DividerView} on the root surface. */
|
||||
@@ -290,7 +291,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
*/
|
||||
void updateDivideBounds(int position) {
|
||||
updateBounds(position);
|
||||
mSplitWindowManager.setResizingSplits(true);
|
||||
mSplitLayoutHandler.onLayoutSizeChanging(this);
|
||||
}
|
||||
|
||||
@@ -298,13 +298,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
mDividePosition = position;
|
||||
updateBounds(mDividePosition);
|
||||
mSplitLayoutHandler.onLayoutSizeChanged(this);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
}
|
||||
|
||||
/** Resets divider position. */
|
||||
public void resetDividerPosition() {
|
||||
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
updateBounds(mDividePosition);
|
||||
mWinToken1 = null;
|
||||
mWinToken2 = null;
|
||||
@@ -360,8 +358,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
@VisibleForTesting
|
||||
void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
|
||||
if (from == to) {
|
||||
// No animation run, it should stop resizing here.
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
// No animation run, still callback to stop resizing.
|
||||
mSplitLayoutHandler.onLayoutSizeChanged(this);
|
||||
return;
|
||||
}
|
||||
ValueAnimator animator = ValueAnimator
|
||||
@@ -425,7 +423,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
return;
|
||||
}
|
||||
|
||||
mDismissingParallaxPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
|
||||
mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
|
||||
}
|
||||
|
||||
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
|
||||
@@ -543,7 +541,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
* Calculates and applies proper dismissing parallax offset and dimming value to hint users
|
||||
* dismissing gesture.
|
||||
*/
|
||||
private class DismissingParallaxPolicy {
|
||||
private class DismissingEffectPolicy {
|
||||
/** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
|
||||
private final boolean mApplyParallax;
|
||||
|
||||
// The current dismissing side.
|
||||
int mDismissingSide = DOCKED_INVALID;
|
||||
|
||||
@@ -553,6 +554,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
// The dimming value to hint the dismissing side and progress.
|
||||
float mDismissingDimValue = 0.0f;
|
||||
|
||||
DismissingEffectPolicy(boolean applyDismissingParallax) {
|
||||
mApplyParallax = applyDismissingParallax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a parallax to the task to hint dismissing progress.
|
||||
*
|
||||
@@ -627,12 +632,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
return false;
|
||||
}
|
||||
|
||||
t.setPosition(targetLeash,
|
||||
mTempRect.left + mDismissingParallaxOffset.x,
|
||||
mTempRect.top + mDismissingParallaxOffset.y);
|
||||
// Transform the screen-based split bounds to surface-based crop bounds.
|
||||
mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
|
||||
t.setWindowCrop(targetLeash, mTempRect);
|
||||
if (mApplyParallax) {
|
||||
t.setPosition(targetLeash,
|
||||
mTempRect.left + mDismissingParallaxOffset.x,
|
||||
mTempRect.top + mDismissingParallaxOffset.y);
|
||||
// Transform the screen-based split bounds to surface-based crop bounds.
|
||||
mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
|
||||
t.setWindowCrop(targetLeash, mTempRect);
|
||||
}
|
||||
t.setAlpha(targetDimLayer, mDismissingDimValue)
|
||||
.setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
|
||||
return true;
|
||||
|
||||
@@ -25,7 +25,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_DOCK_DIVIDER;
|
||||
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PixelFormat;
|
||||
@@ -33,8 +32,6 @@ import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.view.IWindow;
|
||||
import android.view.InsetsState;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -59,7 +56,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
private Context mContext;
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
private SurfaceControl mLeash;
|
||||
private boolean mResizingSplits;
|
||||
private DividerView mDividerView;
|
||||
|
||||
public interface ParentContainerCallbacks {
|
||||
@@ -154,16 +150,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
mDividerView.setInteractive(interactive);
|
||||
}
|
||||
|
||||
void setResizingSplits(boolean resizing) {
|
||||
if (resizing == mResizingSplits) return;
|
||||
try {
|
||||
ActivityTaskManager.getService().setSplitScreenResizing(resizing);
|
||||
mResizingSplits = resizing;
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Error calling setSplitScreenResizing", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
|
||||
* feasible.
|
||||
|
||||
@@ -19,11 +19,13 @@ package com.android.wm.shell.splitscreen;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
@@ -37,11 +39,11 @@ class MainStage extends StageTaskListener {
|
||||
|
||||
private boolean mIsActive = false;
|
||||
|
||||
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession,
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
|
||||
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
|
||||
stageTaskUnfoldController);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,181 +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 static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
|
||||
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.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowlessWindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
class OutlineManager extends WindowlessWindowManager {
|
||||
private static final String WINDOW_NAME = "SplitOutlineLayer";
|
||||
private final Context mContext;
|
||||
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;
|
||||
|
||||
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
|
||||
protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
|
||||
b.setParent(mHostLeash);
|
||||
}
|
||||
|
||||
void inflate(SurfaceControl rootLeash, Rect rootBounds) {
|
||||
if (mLeash != null || mViewHost != null) return;
|
||||
|
||||
mHostLeash = rootLeash;
|
||||
mRootBounds.set(rootBounds);
|
||||
mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
|
||||
|
||||
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(rootLayout, lp);
|
||||
mLeash = getSurfaceControl(mViewHost.getWindowToken());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,82 +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 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.util.AttributeSet;
|
||||
import android.view.RoundedCorner;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
/** View for drawing split outline. */
|
||||
public class OutlineView extends View {
|
||||
private final Paint mPaint = new Paint();
|
||||
private final Path mPath = new Path();
|
||||
private final float[] mRadii = new float[8];
|
||||
|
||||
public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
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 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
canvas.drawPath(mPath, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,16 @@
|
||||
|
||||
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.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.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayInsetsController;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
/**
|
||||
@@ -38,19 +34,15 @@ import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
*
|
||||
* @see StageCoordinator
|
||||
*/
|
||||
class SideStage extends StageTaskListener implements
|
||||
DisplayInsetsController.OnInsetsChangedListener {
|
||||
class SideStage extends StageTaskListener {
|
||||
private static final String TAG = SideStage.class.getSimpleName();
|
||||
private final Context mContext;
|
||||
private OutlineManager mOutlineManager;
|
||||
|
||||
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession,
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
|
||||
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
|
||||
stageTaskUnfoldController);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
|
||||
@@ -83,62 +75,4 @@ class SideStage extends StageTaskListener implements
|
||||
wct.reparent(task.token, newParent, false /* onTop */);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SurfaceControl getOutlineLeash() {
|
||||
return mOutlineManager.getOutlineLeash();
|
||||
}
|
||||
|
||||
@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 (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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.logging.InstanceId;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
@@ -78,6 +79,7 @@ import javax.inject.Provider;
|
||||
/**
|
||||
* Class manages split-screen multitasking mode and implements the main interface
|
||||
* {@link SplitScreen}.
|
||||
*
|
||||
* @see StageCoordinator
|
||||
*/
|
||||
// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
|
||||
@@ -96,6 +98,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
private final Transitions mTransitions;
|
||||
private final TransactionPool mTransactionPool;
|
||||
private final SplitscreenEventLogger mLogger;
|
||||
private final IconProvider mIconProvider;
|
||||
private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
|
||||
|
||||
private StageCoordinator mStageCoordinator;
|
||||
@@ -105,7 +108,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
|
||||
ShellExecutor mainExecutor, DisplayImeController displayImeController,
|
||||
DisplayInsetsController displayInsetsController,
|
||||
Transitions transitions, TransactionPool transactionPool,
|
||||
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
|
||||
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
||||
mTaskOrganizer = shellTaskOrganizer;
|
||||
mSyncQueue = syncQueue;
|
||||
@@ -118,6 +121,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
mTransactionPool = transactionPool;
|
||||
mUnfoldControllerProvider = unfoldControllerProvider;
|
||||
mLogger = new SplitscreenEventLogger();
|
||||
mIconProvider = iconProvider;
|
||||
}
|
||||
|
||||
public SplitScreen asSplitScreen() {
|
||||
@@ -140,7 +144,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
|
||||
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
|
||||
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
|
||||
mUnfoldControllerProvider);
|
||||
mIconProvider, mUnfoldControllerProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,10 +169,6 @@ 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, null /* wct */);
|
||||
}
|
||||
@@ -319,9 +319,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
transaction.apply();
|
||||
transaction.close();
|
||||
return new RemoteAnimationTarget[]{
|
||||
mStageCoordinator.getDividerBarLegacyTarget(),
|
||||
mStageCoordinator.getOutlineLegacyTarget()};
|
||||
return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,6 +80,7 @@ import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.InstanceId;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
@@ -151,10 +152,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
/** Whether the device is supporting legacy split or not. */
|
||||
private boolean mUseLegacySplit;
|
||||
|
||||
@SplitScreen.StageType private int mDismissTop = NO_DISMISS;
|
||||
@SplitScreen.StageType
|
||||
private int mDismissTop = NO_DISMISS;
|
||||
|
||||
/** The target stage to dismiss to when unlock after folded. */
|
||||
@SplitScreen.StageType private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
|
||||
@SplitScreen.StageType
|
||||
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
|
||||
|
||||
private final Runnable mOnTransitionAnimationComplete = () -> {
|
||||
// If still playing, let it finish.
|
||||
@@ -169,22 +172,23 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
|
||||
new SplitWindowManager.ParentContainerCallbacks() {
|
||||
@Override
|
||||
public void attachToParentSurface(SurfaceControl.Builder b) {
|
||||
mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
|
||||
}
|
||||
@Override
|
||||
public void attachToParentSurface(SurfaceControl.Builder b) {
|
||||
mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLeashReady(SurfaceControl leash) {
|
||||
mSyncQueue.runInSync(t -> applyDividerVisibility(t));
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void onLeashReady(SurfaceControl leash) {
|
||||
mSyncQueue.runInSync(t -> applyDividerVisibility(t));
|
||||
}
|
||||
};
|
||||
|
||||
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
|
||||
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
|
||||
DisplayImeController displayImeController,
|
||||
DisplayInsetsController displayInsetsController, Transitions transitions,
|
||||
TransactionPool transactionPool, SplitscreenEventLogger logger,
|
||||
IconProvider iconProvider,
|
||||
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
||||
mContext = context;
|
||||
mDisplayId = displayId;
|
||||
@@ -196,11 +200,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
|
||||
|
||||
mMainStage = new MainStage(
|
||||
mContext,
|
||||
mTaskOrganizer,
|
||||
mDisplayId,
|
||||
mMainStageListener,
|
||||
mSyncQueue,
|
||||
mSurfaceSession,
|
||||
iconProvider,
|
||||
mMainUnfoldController);
|
||||
mSideStage = new SideStage(
|
||||
mContext,
|
||||
@@ -209,10 +215,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSideStageListener,
|
||||
mSyncQueue,
|
||||
mSurfaceSession,
|
||||
iconProvider,
|
||||
mSideUnfoldController);
|
||||
mDisplayImeController = displayImeController;
|
||||
mDisplayInsetsController = displayInsetsController;
|
||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
|
||||
mRootTDAOrganizer.registerListener(displayId, this);
|
||||
final DeviceStateManager deviceStateManager =
|
||||
mContext.getSystemService(DeviceStateManager.class);
|
||||
@@ -284,10 +290,6 @@ 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,
|
||||
@@ -697,9 +699,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
if (bothStageInvisible) {
|
||||
if (mExitSplitScreenOnHide
|
||||
// Don't dismiss staged split when both stages are not visible due to sleeping display,
|
||||
// like the cases keyguard showing or screen off.
|
||||
|| (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
|
||||
// Don't dismiss staged split when both stages are not visible due to sleeping
|
||||
// display, like the cases keyguard showing or screen off.
|
||||
|| (!mMainStage.mRootTaskInfo.isSleeping
|
||||
&& !mSideStage.mRootTaskInfo.isSleeping)) {
|
||||
exitSplitScreen(null /* childrenToTop */,
|
||||
SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
|
||||
}
|
||||
@@ -719,7 +722,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
|
||||
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
|
||||
applyDividerVisibility(t);
|
||||
applyOutlineVisibility(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -741,19 +743,6 @@ 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;
|
||||
@@ -817,8 +806,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
@Override
|
||||
public void onLayoutSizeChanging(SplitLayout layout) {
|
||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
||||
mSideStage.setOutlineVisibility(false);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
updateSurfaceBounds(layout, t);
|
||||
mMainStage.onResizing(getMainStageBounds(), t);
|
||||
mSideStage.onResizing(getSideStageBounds(), t);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -827,8 +819,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
updateWindowBounds(layout, wct);
|
||||
updateUnfoldBounds();
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
||||
mSideStage.setOutlineVisibility(true);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
updateSurfaceBounds(layout, t);
|
||||
mMainStage.onResized(getMainStageBounds(), t);
|
||||
mSideStage.onResized(getSideStageBounds(), t);
|
||||
});
|
||||
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
|
||||
}
|
||||
|
||||
@@ -893,7 +888,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
if (mSplitLayout == null) {
|
||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
||||
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
|
||||
mDisplayImeController, mTaskOrganizer);
|
||||
mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */);
|
||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
|
||||
|
||||
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
||||
@@ -1216,18 +1211,6 @@ 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 + " ";
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIO
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseArray;
|
||||
@@ -35,9 +36,11 @@ import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.SurfaceUtils;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.split.SplitDecorManager;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -72,25 +75,31 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
void onNoLongerSupportMultiWindow();
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final StageListenerCallbacks mCallbacks;
|
||||
private final SurfaceSession mSurfaceSession;
|
||||
protected final SyncTransactionQueue mSyncQueue;
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
private final IconProvider mIconProvider;
|
||||
|
||||
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||
protected SurfaceControl mRootLeash;
|
||||
protected SurfaceControl mDimLayer;
|
||||
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
|
||||
private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
|
||||
// TODO(b/204308910): Extracts SplitDecorManager related code to common package.
|
||||
private SplitDecorManager mSplitDecorManager;
|
||||
|
||||
private final StageTaskUnfoldController mStageTaskUnfoldController;
|
||||
|
||||
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession,
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||
mContext = context;
|
||||
mCallbacks = callbacks;
|
||||
mSyncQueue = syncQueue;
|
||||
mSurfaceSession = surfaceSession;
|
||||
mIconProvider = iconProvider;
|
||||
mStageTaskUnfoldController = stageTaskUnfoldController;
|
||||
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
|
||||
}
|
||||
@@ -142,13 +151,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
|
||||
mRootLeash = leash;
|
||||
mRootTaskInfo = taskInfo;
|
||||
mSplitDecorManager = new SplitDecorManager(
|
||||
mRootTaskInfo.configuration,
|
||||
mIconProvider,
|
||||
mSurfaceSession);
|
||||
mCallbacks.onRootTaskAppeared();
|
||||
sendStatusChanged();
|
||||
mSyncQueue.runInSync(t -> {
|
||||
t.hide(mRootLeash);
|
||||
mDimLayer =
|
||||
SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession);
|
||||
});
|
||||
mSyncQueue.runInSync(t -> mDimLayer =
|
||||
SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
|
||||
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
||||
final int taskId = taskInfo.taskId;
|
||||
mChildrenLeashes.put(taskId, leash);
|
||||
@@ -179,6 +189,17 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
return;
|
||||
}
|
||||
if (mRootTaskInfo.taskId == taskInfo.taskId) {
|
||||
// Inflates split decor view only when the root task is visible.
|
||||
if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
|
||||
mSyncQueue.runInSync(t -> {
|
||||
if (taskInfo.isVisible) {
|
||||
mSplitDecorManager.inflate(mContext, mRootLeash,
|
||||
taskInfo.configuration.windowConfiguration.getBounds());
|
||||
} else {
|
||||
mSplitDecorManager.release(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
mRootTaskInfo = taskInfo;
|
||||
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
||||
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
|
||||
@@ -205,8 +226,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
final int taskId = taskInfo.taskId;
|
||||
if (mRootTaskInfo.taskId == taskId) {
|
||||
mCallbacks.onRootTaskVanished();
|
||||
mSyncQueue.runInSync(t -> t.remove(mDimLayer));
|
||||
mRootTaskInfo = null;
|
||||
mSyncQueue.runInSync(t -> {
|
||||
t.remove(mDimLayer);
|
||||
mSplitDecorManager.release(t);
|
||||
});
|
||||
} else if (mChildrenTaskInfo.contains(taskId)) {
|
||||
mChildrenTaskInfo.remove(taskId);
|
||||
mChildrenLeashes.remove(taskId);
|
||||
@@ -237,6 +261,18 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
}
|
||||
}
|
||||
|
||||
void onResizing(Rect newBounds, SurfaceControl.Transaction t) {
|
||||
if (mSplitDecorManager != null && mRootTaskInfo != null) {
|
||||
mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, t);
|
||||
}
|
||||
}
|
||||
|
||||
void onResized(Rect newBounds, SurfaceControl.Transaction t) {
|
||||
if (mSplitDecorManager != null) {
|
||||
mSplitDecorManager.onResized(newBounds, t);
|
||||
}
|
||||
}
|
||||
|
||||
void setBounds(Rect bounds, WindowContainerTransaction wct) {
|
||||
wct.setBounds(mRootTaskInfo.token, bounds);
|
||||
}
|
||||
|
||||
@@ -882,7 +882,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
if (mSplitLayout == null) {
|
||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
||||
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
|
||||
mDisplayImeController, mTaskOrganizer);
|
||||
mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */);
|
||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
|
||||
|
||||
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
||||
|
||||
@@ -68,7 +68,8 @@ public class SplitLayoutTests extends ShellTestCase {
|
||||
mSplitLayoutHandler,
|
||||
mCallbacks,
|
||||
mDisplayImeController,
|
||||
mTaskOrganizer));
|
||||
mTaskOrganizer,
|
||||
false /* applyDismissingParallax */));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -25,10 +25,13 @@ 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;
|
||||
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
@@ -41,22 +44,24 @@ import org.mockito.MockitoAnnotations;
|
||||
/** Tests for {@link MainStage} */
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MainStageTests {
|
||||
public class MainStageTests extends ShellTestCase {
|
||||
@Mock private ShellTaskOrganizer mTaskOrganizer;
|
||||
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
|
||||
@Mock private SyncTransactionQueue mSyncQueue;
|
||||
@Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||
@Mock private SurfaceControl mRootLeash;
|
||||
@Mock private IconProvider mIconProvider;
|
||||
private WindowContainerTransaction mWct = new WindowContainerTransaction();
|
||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private MainStage mMainStage;
|
||||
|
||||
@Before
|
||||
@UiThreadTest
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
|
||||
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
|
||||
mSurfaceSession, null);
|
||||
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
|
||||
mSyncQueue, mSurfaceSession, mIconProvider, null);
|
||||
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||
@@ -54,6 +55,7 @@ public class SideStageTests extends ShellTestCase {
|
||||
@Mock private SyncTransactionQueue mSyncQueue;
|
||||
@Mock private ActivityManager.RunningTaskInfo mRootTask;
|
||||
@Mock private SurfaceControl mRootLeash;
|
||||
@Mock private IconProvider mIconProvider;
|
||||
@Spy private WindowContainerTransaction mWct;
|
||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private SideStage mSideStage;
|
||||
@@ -64,7 +66,7 @@ public class SideStageTests extends ShellTestCase {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
|
||||
mSyncQueue, mSurfaceSession, null);
|
||||
mSyncQueue, mSurfaceSession, mIconProvider, null);
|
||||
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
@@ -89,6 +90,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
@Mock private Transitions mTransitions;
|
||||
@Mock private SurfaceSession mSurfaceSession;
|
||||
@Mock private SplitscreenEventLogger mLogger;
|
||||
@Mock private IconProvider mIconProvider;
|
||||
private SplitLayout mSplitLayout;
|
||||
private MainStage mMainStage;
|
||||
private SideStage mSideStage;
|
||||
@@ -107,11 +109,13 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
|
||||
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
|
||||
mSplitLayout = SplitTestUtils.createMockSplitLayout();
|
||||
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
|
||||
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||
mIconProvider, null);
|
||||
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||
mIconProvider, null);
|
||||
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
|
||||
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
|
||||
|
||||
@@ -35,10 +35,13 @@ 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;
|
||||
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
@@ -57,7 +60,7 @@ import org.mockito.MockitoAnnotations;
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class StageTaskListenerTests {
|
||||
public final class StageTaskListenerTests extends ShellTestCase {
|
||||
private static final boolean ENABLE_SHELL_TRANSITIONS =
|
||||
SystemProperties.getBoolean("persist.debug.shell_transit", false);
|
||||
|
||||
@@ -68,6 +71,8 @@ public final class StageTaskListenerTests {
|
||||
@Mock
|
||||
private SyncTransactionQueue mSyncQueue;
|
||||
@Mock
|
||||
private IconProvider mIconProvider;
|
||||
@Mock
|
||||
private StageTaskUnfoldController mStageTaskUnfoldController;
|
||||
@Captor
|
||||
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
|
||||
@@ -77,14 +82,17 @@ public final class StageTaskListenerTests {
|
||||
private StageTaskListener mStageTaskListener;
|
||||
|
||||
@Before
|
||||
@UiThreadTest
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mStageTaskListener = new StageTaskListener(
|
||||
mContext,
|
||||
mTaskOrganizer,
|
||||
DEFAULT_DISPLAY,
|
||||
mCallbacks,
|
||||
mSyncQueue,
|
||||
mSurfaceSession,
|
||||
mIconProvider,
|
||||
mStageTaskUnfoldController);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
mRootTask.parentTaskId = INVALID_TASK_ID;
|
||||
|
||||
@@ -463,13 +463,13 @@ public abstract class WMShellBaseModule {
|
||||
@ShellMainThread ShellExecutor mainExecutor,
|
||||
DisplayImeController displayImeController,
|
||||
DisplayInsetsController displayInsetsController, Transitions transitions,
|
||||
TransactionPool transactionPool,
|
||||
TransactionPool transactionPool, IconProvider iconProvider,
|
||||
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
|
||||
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
|
||||
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
|
||||
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
|
||||
displayInsetsController, transitions,
|
||||
transactionPool, stageTaskUnfoldControllerProvider));
|
||||
displayInsetsController, transitions, transactionPool, iconProvider,
|
||||
stageTaskUnfoldControllerProvider));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user