Merge "Update split resizing transition to match design" into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1679d92eef
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