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,17 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
<!--
|
||||||
|
~ 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.
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
You may obtain a copy of the License at
|
~ 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
|
~
|
||||||
|
~ 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,
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
See the License for the specific language governing permissions and
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
limitations under the License.
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
|||||||
@@ -103,8 +103,6 @@ public final class ShellCommandHandlerImpl {
|
|||||||
return runMoveToSideStage(args, pw);
|
return runMoveToSideStage(args, pw);
|
||||||
case "removeFromSideStage":
|
case "removeFromSideStage":
|
||||||
return runRemoveFromSideStage(args, pw);
|
return runRemoveFromSideStage(args, pw);
|
||||||
case "setSideStageOutline":
|
|
||||||
return runSetSideStageOutline(args, pw);
|
|
||||||
case "setSideStagePosition":
|
case "setSideStagePosition":
|
||||||
return runSetSideStagePosition(args, pw);
|
return runSetSideStagePosition(args, pw);
|
||||||
case "setSideStageVisibility":
|
case "setSideStageVisibility":
|
||||||
@@ -163,18 +161,6 @@ public final class ShellCommandHandlerImpl {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean runSetSideStageOutline(String[] args, PrintWriter pw) {
|
|
||||||
if (args.length < 3) {
|
|
||||||
// First arguments are "WMShell" and command name.
|
|
||||||
pw.println("Error: whether to enable or disable side stage outline border should be"
|
|
||||||
+ " provided as arguments");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final boolean enable = new Boolean(args[2]);
|
|
||||||
mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
|
private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
|
||||||
if (args.length < 3) {
|
if (args.length < 3) {
|
||||||
// First arguments are "WMShell" and command name.
|
// First arguments are "WMShell" and command name.
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
|
|||||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
|
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
|
||||||
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
|
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
|
||||||
mRootTaskInfo.configuration, this /* layoutChangeListener */,
|
mRootTaskInfo.configuration, this /* layoutChangeListener */,
|
||||||
mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
|
mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
|
||||||
|
true /* applyDismissingParallax */);
|
||||||
mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
|
mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
|
||||||
|
|
||||||
final WindowContainerToken token1 = task1.token;
|
final WindowContainerToken token1 = task1.token;
|
||||||
|
|||||||
@@ -23,16 +23,22 @@ import android.view.SurfaceSession;
|
|||||||
* Helpers for handling surface.
|
* Helpers for handling surface.
|
||||||
*/
|
*/
|
||||||
public class SurfaceUtils {
|
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,
|
public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host,
|
||||||
String name, SurfaceSession surfaceSession) {
|
String name, SurfaceSession surfaceSession) {
|
||||||
SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession)
|
final SurfaceControl dimLayer = makeColorLayer(host, name, surfaceSession);
|
||||||
.setParent(host)
|
|
||||||
.setColorLayer()
|
|
||||||
.setName(name)
|
|
||||||
.setCallsite("SurfaceUtils.makeDimLayer")
|
|
||||||
.build();
|
|
||||||
t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
|
t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
|
||||||
return dimLayer;
|
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 SplitWindowManager mSplitWindowManager;
|
||||||
private final DisplayImeController mDisplayImeController;
|
private final DisplayImeController mDisplayImeController;
|
||||||
private final ImePositionProcessor mImePositionProcessor;
|
private final ImePositionProcessor mImePositionProcessor;
|
||||||
private final DismissingParallaxPolicy mDismissingParallaxPolicy;
|
private final DismissingEffectPolicy mDismissingEffectPolicy;
|
||||||
private final ShellTaskOrganizer mTaskOrganizer;
|
private final ShellTaskOrganizer mTaskOrganizer;
|
||||||
private final InsetsState mInsetsState = new InsetsState();
|
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,
|
public SplitLayout(String windowName, Context context, Configuration configuration,
|
||||||
SplitLayoutHandler splitLayoutHandler,
|
SplitLayoutHandler splitLayoutHandler,
|
||||||
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
|
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
|
||||||
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
|
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer,
|
||||||
|
boolean applyDismissingParallax) {
|
||||||
mContext = context.createConfigurationContext(configuration);
|
mContext = context.createConfigurationContext(configuration);
|
||||||
mOrientation = configuration.orientation;
|
mOrientation = configuration.orientation;
|
||||||
mRotation = configuration.windowConfiguration.getRotation();
|
mRotation = configuration.windowConfiguration.getRotation();
|
||||||
@@ -129,7 +130,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
parentContainerCallbacks);
|
parentContainerCallbacks);
|
||||||
mTaskOrganizer = taskOrganizer;
|
mTaskOrganizer = taskOrganizer;
|
||||||
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
|
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
|
||||||
mDismissingParallaxPolicy = new DismissingParallaxPolicy();
|
mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax);
|
||||||
|
|
||||||
final Resources resources = context.getResources();
|
final Resources resources = context.getResources();
|
||||||
mDividerWindowWidth = resources.getDimensionPixelSize(
|
mDividerWindowWidth = resources.getDimensionPixelSize(
|
||||||
@@ -247,7 +248,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
}
|
}
|
||||||
DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
|
DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
|
||||||
DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
|
DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
|
||||||
mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape);
|
mDismissingEffectPolicy.applyDividerPosition(position, isLandscape);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Inflates {@link DividerView} on the root surface. */
|
/** Inflates {@link DividerView} on the root surface. */
|
||||||
@@ -290,7 +291,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
*/
|
*/
|
||||||
void updateDivideBounds(int position) {
|
void updateDivideBounds(int position) {
|
||||||
updateBounds(position);
|
updateBounds(position);
|
||||||
mSplitWindowManager.setResizingSplits(true);
|
|
||||||
mSplitLayoutHandler.onLayoutSizeChanging(this);
|
mSplitLayoutHandler.onLayoutSizeChanging(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,13 +298,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
mDividePosition = position;
|
mDividePosition = position;
|
||||||
updateBounds(mDividePosition);
|
updateBounds(mDividePosition);
|
||||||
mSplitLayoutHandler.onLayoutSizeChanged(this);
|
mSplitLayoutHandler.onLayoutSizeChanged(this);
|
||||||
mSplitWindowManager.setResizingSplits(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resets divider position. */
|
/** Resets divider position. */
|
||||||
public void resetDividerPosition() {
|
public void resetDividerPosition() {
|
||||||
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
|
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
|
||||||
mSplitWindowManager.setResizingSplits(false);
|
|
||||||
updateBounds(mDividePosition);
|
updateBounds(mDividePosition);
|
||||||
mWinToken1 = null;
|
mWinToken1 = null;
|
||||||
mWinToken2 = null;
|
mWinToken2 = null;
|
||||||
@@ -360,8 +358,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
|
void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
|
||||||
if (from == to) {
|
if (from == to) {
|
||||||
// No animation run, it should stop resizing here.
|
// No animation run, still callback to stop resizing.
|
||||||
mSplitWindowManager.setResizingSplits(false);
|
mSplitLayoutHandler.onLayoutSizeChanged(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ValueAnimator animator = ValueAnimator
|
ValueAnimator animator = ValueAnimator
|
||||||
@@ -425,7 +423,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mDismissingParallaxPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
|
mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
|
/** 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
|
* Calculates and applies proper dismissing parallax offset and dimming value to hint users
|
||||||
* dismissing gesture.
|
* 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.
|
// The current dismissing side.
|
||||||
int mDismissingSide = DOCKED_INVALID;
|
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.
|
// The dimming value to hint the dismissing side and progress.
|
||||||
float mDismissingDimValue = 0.0f;
|
float mDismissingDimValue = 0.0f;
|
||||||
|
|
||||||
|
DismissingEffectPolicy(boolean applyDismissingParallax) {
|
||||||
|
mApplyParallax = applyDismissingParallax;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies a parallax to the task to hint dismissing progress.
|
* Applies a parallax to the task to hint dismissing progress.
|
||||||
*
|
*
|
||||||
@@ -627,12 +632,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mApplyParallax) {
|
||||||
t.setPosition(targetLeash,
|
t.setPosition(targetLeash,
|
||||||
mTempRect.left + mDismissingParallaxOffset.x,
|
mTempRect.left + mDismissingParallaxOffset.x,
|
||||||
mTempRect.top + mDismissingParallaxOffset.y);
|
mTempRect.top + mDismissingParallaxOffset.y);
|
||||||
// Transform the screen-based split bounds to surface-based crop bounds.
|
// Transform the screen-based split bounds to surface-based crop bounds.
|
||||||
mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
|
mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
|
||||||
t.setWindowCrop(targetLeash, mTempRect);
|
t.setWindowCrop(targetLeash, mTempRect);
|
||||||
|
}
|
||||||
t.setAlpha(targetDimLayer, mDismissingDimValue)
|
t.setAlpha(targetDimLayer, mDismissingDimValue)
|
||||||
.setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
|
.setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
|
||||||
return true;
|
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.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
|
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
|
||||||
|
|
||||||
import android.app.ActivityTaskManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
@@ -33,8 +32,6 @@ import android.graphics.Rect;
|
|||||||
import android.graphics.Region;
|
import android.graphics.Region;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Slog;
|
|
||||||
import android.view.IWindow;
|
import android.view.IWindow;
|
||||||
import android.view.InsetsState;
|
import android.view.InsetsState;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -59,7 +56,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
|||||||
private Context mContext;
|
private Context mContext;
|
||||||
private SurfaceControlViewHost mViewHost;
|
private SurfaceControlViewHost mViewHost;
|
||||||
private SurfaceControl mLeash;
|
private SurfaceControl mLeash;
|
||||||
private boolean mResizingSplits;
|
|
||||||
private DividerView mDividerView;
|
private DividerView mDividerView;
|
||||||
|
|
||||||
public interface ParentContainerCallbacks {
|
public interface ParentContainerCallbacks {
|
||||||
@@ -154,16 +150,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
|||||||
mDividerView.setInteractive(interactive);
|
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
|
* Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
|
||||||
* feasible.
|
* feasible.
|
||||||
|
|||||||
@@ -19,11 +19,13 @@ package com.android.wm.shell.splitscreen;
|
|||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.view.SurfaceSession;
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
|
||||||
@@ -37,11 +39,11 @@ class MainStage extends StageTaskListener {
|
|||||||
|
|
||||||
private boolean mIsActive = false;
|
private boolean mIsActive = false;
|
||||||
|
|
||||||
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||||
SurfaceSession surfaceSession,
|
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||||
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
|
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
|
||||||
stageTaskUnfoldController);
|
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;
|
package com.android.wm.shell.splitscreen;
|
||||||
|
|
||||||
import android.annotation.CallSuper;
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.view.InsetsSourceControl;
|
|
||||||
import android.view.InsetsState;
|
|
||||||
import android.view.SurfaceControl;
|
|
||||||
import android.view.SurfaceSession;
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayInsetsController;
|
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,19 +34,15 @@ import com.android.wm.shell.common.SyncTransactionQueue;
|
|||||||
*
|
*
|
||||||
* @see StageCoordinator
|
* @see StageCoordinator
|
||||||
*/
|
*/
|
||||||
class SideStage extends StageTaskListener implements
|
class SideStage extends StageTaskListener {
|
||||||
DisplayInsetsController.OnInsetsChangedListener {
|
|
||||||
private static final String TAG = SideStage.class.getSimpleName();
|
private static final String TAG = SideStage.class.getSimpleName();
|
||||||
private final Context mContext;
|
|
||||||
private OutlineManager mOutlineManager;
|
|
||||||
|
|
||||||
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||||
SurfaceSession surfaceSession,
|
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||||
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
|
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
|
||||||
stageTaskUnfoldController);
|
stageTaskUnfoldController);
|
||||||
mContext = context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
|
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
|
||||||
@@ -83,62 +75,4 @@ class SideStage extends StageTaskListener implements
|
|||||||
wct.reparent(task.token, newParent, false /* onTop */);
|
wct.reparent(task.token, newParent, false /* onTop */);
|
||||||
return true;
|
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.logging.InstanceId;
|
||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayImeController;
|
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
|
* Class manages split-screen multitasking mode and implements the main interface
|
||||||
* {@link SplitScreen}.
|
* {@link SplitScreen}.
|
||||||
|
*
|
||||||
* @see StageCoordinator
|
* @see StageCoordinator
|
||||||
*/
|
*/
|
||||||
// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
|
// 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 Transitions mTransitions;
|
||||||
private final TransactionPool mTransactionPool;
|
private final TransactionPool mTransactionPool;
|
||||||
private final SplitscreenEventLogger mLogger;
|
private final SplitscreenEventLogger mLogger;
|
||||||
|
private final IconProvider mIconProvider;
|
||||||
private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
|
private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
|
||||||
|
|
||||||
private StageCoordinator mStageCoordinator;
|
private StageCoordinator mStageCoordinator;
|
||||||
@@ -105,7 +108,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
|
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
|
||||||
ShellExecutor mainExecutor, DisplayImeController displayImeController,
|
ShellExecutor mainExecutor, DisplayImeController displayImeController,
|
||||||
DisplayInsetsController displayInsetsController,
|
DisplayInsetsController displayInsetsController,
|
||||||
Transitions transitions, TransactionPool transactionPool,
|
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
|
||||||
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
||||||
mTaskOrganizer = shellTaskOrganizer;
|
mTaskOrganizer = shellTaskOrganizer;
|
||||||
mSyncQueue = syncQueue;
|
mSyncQueue = syncQueue;
|
||||||
@@ -118,6 +121,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
mTransactionPool = transactionPool;
|
mTransactionPool = transactionPool;
|
||||||
mUnfoldControllerProvider = unfoldControllerProvider;
|
mUnfoldControllerProvider = unfoldControllerProvider;
|
||||||
mLogger = new SplitscreenEventLogger();
|
mLogger = new SplitscreenEventLogger();
|
||||||
|
mIconProvider = iconProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SplitScreen asSplitScreen() {
|
public SplitScreen asSplitScreen() {
|
||||||
@@ -140,7 +144,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
|
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
|
||||||
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
|
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
|
||||||
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
|
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
|
||||||
mUnfoldControllerProvider);
|
mIconProvider, mUnfoldControllerProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,10 +169,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
return mStageCoordinator.removeFromSideStage(taskId);
|
return mStageCoordinator.removeFromSideStage(taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSideStageOutline(boolean enable) {
|
|
||||||
mStageCoordinator.setSideStageOutline(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
|
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
|
||||||
mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
|
mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
|
||||||
}
|
}
|
||||||
@@ -319,9 +319,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
}
|
}
|
||||||
transaction.apply();
|
transaction.apply();
|
||||||
transaction.close();
|
transaction.close();
|
||||||
return new RemoteAnimationTarget[]{
|
return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
|
||||||
mStageCoordinator.getDividerBarLegacyTarget(),
|
|
||||||
mStageCoordinator.getOutlineLegacyTarget()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ import com.android.internal.R;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.logging.InstanceId;
|
import com.android.internal.logging.InstanceId;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayImeController;
|
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. */
|
/** Whether the device is supporting legacy split or not. */
|
||||||
private boolean mUseLegacySplit;
|
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. */
|
/** 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 = () -> {
|
private final Runnable mOnTransitionAnimationComplete = () -> {
|
||||||
// If still playing, let it finish.
|
// If still playing, let it finish.
|
||||||
@@ -185,6 +188,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
DisplayImeController displayImeController,
|
DisplayImeController displayImeController,
|
||||||
DisplayInsetsController displayInsetsController, Transitions transitions,
|
DisplayInsetsController displayInsetsController, Transitions transitions,
|
||||||
TransactionPool transactionPool, SplitscreenEventLogger logger,
|
TransactionPool transactionPool, SplitscreenEventLogger logger,
|
||||||
|
IconProvider iconProvider,
|
||||||
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mDisplayId = displayId;
|
mDisplayId = displayId;
|
||||||
@@ -196,11 +200,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
|
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
|
||||||
|
|
||||||
mMainStage = new MainStage(
|
mMainStage = new MainStage(
|
||||||
|
mContext,
|
||||||
mTaskOrganizer,
|
mTaskOrganizer,
|
||||||
mDisplayId,
|
mDisplayId,
|
||||||
mMainStageListener,
|
mMainStageListener,
|
||||||
mSyncQueue,
|
mSyncQueue,
|
||||||
mSurfaceSession,
|
mSurfaceSession,
|
||||||
|
iconProvider,
|
||||||
mMainUnfoldController);
|
mMainUnfoldController);
|
||||||
mSideStage = new SideStage(
|
mSideStage = new SideStage(
|
||||||
mContext,
|
mContext,
|
||||||
@@ -209,10 +215,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
mSideStageListener,
|
mSideStageListener,
|
||||||
mSyncQueue,
|
mSyncQueue,
|
||||||
mSurfaceSession,
|
mSurfaceSession,
|
||||||
|
iconProvider,
|
||||||
mSideUnfoldController);
|
mSideUnfoldController);
|
||||||
mDisplayImeController = displayImeController;
|
mDisplayImeController = displayImeController;
|
||||||
mDisplayInsetsController = displayInsetsController;
|
mDisplayInsetsController = displayInsetsController;
|
||||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
|
|
||||||
mRootTDAOrganizer.registerListener(displayId, this);
|
mRootTDAOrganizer.registerListener(displayId, this);
|
||||||
final DeviceStateManager deviceStateManager =
|
final DeviceStateManager deviceStateManager =
|
||||||
mContext.getSystemService(DeviceStateManager.class);
|
mContext.getSystemService(DeviceStateManager.class);
|
||||||
@@ -284,10 +290,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSideStageOutline(boolean enable) {
|
|
||||||
mSideStage.enableOutline(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Starts 2 tasks in one transition. */
|
/** Starts 2 tasks in one transition. */
|
||||||
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
|
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
|
||||||
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
|
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
|
||||||
@@ -697,9 +699,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
|
|
||||||
if (bothStageInvisible) {
|
if (bothStageInvisible) {
|
||||||
if (mExitSplitScreenOnHide
|
if (mExitSplitScreenOnHide
|
||||||
// Don't dismiss staged split when both stages are not visible due to sleeping display,
|
// Don't dismiss staged split when both stages are not visible due to sleeping
|
||||||
// like the cases keyguard showing or screen off.
|
// display, like the cases keyguard showing or screen off.
|
||||||
|| (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
|
|| (!mMainStage.mRootTaskInfo.isSleeping
|
||||||
|
&& !mSideStage.mRootTaskInfo.isSleeping)) {
|
||||||
exitSplitScreen(null /* childrenToTop */,
|
exitSplitScreen(null /* childrenToTop */,
|
||||||
SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
|
SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
|
||||||
}
|
}
|
||||||
@@ -719,7 +722,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
|
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
|
||||||
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
|
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
|
||||||
applyDividerVisibility(t);
|
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) {
|
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
|
||||||
final boolean hasChildren = stageListener.mHasChildren;
|
final boolean hasChildren = stageListener.mHasChildren;
|
||||||
final boolean isSideStage = stageListener == mSideStageListener;
|
final boolean isSideStage = stageListener == mSideStageListener;
|
||||||
@@ -817,8 +806,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLayoutSizeChanging(SplitLayout layout) {
|
public void onLayoutSizeChanging(SplitLayout layout) {
|
||||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
mSyncQueue.runInSync(t -> {
|
||||||
mSideStage.setOutlineVisibility(false);
|
updateSurfaceBounds(layout, t);
|
||||||
|
mMainStage.onResizing(getMainStageBounds(), t);
|
||||||
|
mSideStage.onResizing(getSideStageBounds(), t);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -827,8 +819,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
updateWindowBounds(layout, wct);
|
updateWindowBounds(layout, wct);
|
||||||
updateUnfoldBounds();
|
updateUnfoldBounds();
|
||||||
mSyncQueue.queue(wct);
|
mSyncQueue.queue(wct);
|
||||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
mSyncQueue.runInSync(t -> {
|
||||||
mSideStage.setOutlineVisibility(true);
|
updateSurfaceBounds(layout, t);
|
||||||
|
mMainStage.onResized(getMainStageBounds(), t);
|
||||||
|
mSideStage.onResized(getSideStageBounds(), t);
|
||||||
|
});
|
||||||
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
|
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,7 +888,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
if (mSplitLayout == null) {
|
if (mSplitLayout == null) {
|
||||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
||||||
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
|
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
|
||||||
mDisplayImeController, mTaskOrganizer);
|
mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */);
|
||||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
|
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
|
||||||
|
|
||||||
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
||||||
@@ -1216,18 +1211,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
|
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
|
@Override
|
||||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||||
final String innerPrefix = 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.CallSuper;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
import android.content.Context;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@@ -35,9 +36,11 @@ import android.window.WindowContainerTransaction;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.SurfaceUtils;
|
import com.android.wm.shell.common.SurfaceUtils;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
import com.android.wm.shell.common.split.SplitDecorManager;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
@@ -72,25 +75,31 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
void onNoLongerSupportMultiWindow();
|
void onNoLongerSupportMultiWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
private final StageListenerCallbacks mCallbacks;
|
private final StageListenerCallbacks mCallbacks;
|
||||||
private final SurfaceSession mSurfaceSession;
|
private final SurfaceSession mSurfaceSession;
|
||||||
protected final SyncTransactionQueue mSyncQueue;
|
private final SyncTransactionQueue mSyncQueue;
|
||||||
|
private final IconProvider mIconProvider;
|
||||||
|
|
||||||
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
|
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||||
protected SurfaceControl mRootLeash;
|
protected SurfaceControl mRootLeash;
|
||||||
protected SurfaceControl mDimLayer;
|
protected SurfaceControl mDimLayer;
|
||||||
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
|
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
|
||||||
private final SparseArray<SurfaceControl> mChildrenLeashes = 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;
|
private final StageTaskUnfoldController mStageTaskUnfoldController;
|
||||||
|
|
||||||
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
|
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||||
SurfaceSession surfaceSession,
|
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||||
|
mContext = context;
|
||||||
mCallbacks = callbacks;
|
mCallbacks = callbacks;
|
||||||
mSyncQueue = syncQueue;
|
mSyncQueue = syncQueue;
|
||||||
mSurfaceSession = surfaceSession;
|
mSurfaceSession = surfaceSession;
|
||||||
|
mIconProvider = iconProvider;
|
||||||
mStageTaskUnfoldController = stageTaskUnfoldController;
|
mStageTaskUnfoldController = stageTaskUnfoldController;
|
||||||
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
|
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
|
||||||
}
|
}
|
||||||
@@ -142,13 +151,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
|
if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
|
||||||
mRootLeash = leash;
|
mRootLeash = leash;
|
||||||
mRootTaskInfo = taskInfo;
|
mRootTaskInfo = taskInfo;
|
||||||
|
mSplitDecorManager = new SplitDecorManager(
|
||||||
|
mRootTaskInfo.configuration,
|
||||||
|
mIconProvider,
|
||||||
|
mSurfaceSession);
|
||||||
mCallbacks.onRootTaskAppeared();
|
mCallbacks.onRootTaskAppeared();
|
||||||
sendStatusChanged();
|
sendStatusChanged();
|
||||||
mSyncQueue.runInSync(t -> {
|
mSyncQueue.runInSync(t -> mDimLayer =
|
||||||
t.hide(mRootLeash);
|
SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
|
||||||
mDimLayer =
|
|
||||||
SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession);
|
|
||||||
});
|
|
||||||
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
||||||
final int taskId = taskInfo.taskId;
|
final int taskId = taskInfo.taskId;
|
||||||
mChildrenLeashes.put(taskId, leash);
|
mChildrenLeashes.put(taskId, leash);
|
||||||
@@ -179,6 +189,17 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mRootTaskInfo.taskId == taskInfo.taskId) {
|
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;
|
mRootTaskInfo = taskInfo;
|
||||||
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
||||||
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
|
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
|
||||||
@@ -205,8 +226,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
final int taskId = taskInfo.taskId;
|
final int taskId = taskInfo.taskId;
|
||||||
if (mRootTaskInfo.taskId == taskId) {
|
if (mRootTaskInfo.taskId == taskId) {
|
||||||
mCallbacks.onRootTaskVanished();
|
mCallbacks.onRootTaskVanished();
|
||||||
mSyncQueue.runInSync(t -> t.remove(mDimLayer));
|
|
||||||
mRootTaskInfo = null;
|
mRootTaskInfo = null;
|
||||||
|
mSyncQueue.runInSync(t -> {
|
||||||
|
t.remove(mDimLayer);
|
||||||
|
mSplitDecorManager.release(t);
|
||||||
|
});
|
||||||
} else if (mChildrenTaskInfo.contains(taskId)) {
|
} else if (mChildrenTaskInfo.contains(taskId)) {
|
||||||
mChildrenTaskInfo.remove(taskId);
|
mChildrenTaskInfo.remove(taskId);
|
||||||
mChildrenLeashes.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) {
|
void setBounds(Rect bounds, WindowContainerTransaction wct) {
|
||||||
wct.setBounds(mRootTaskInfo.token, bounds);
|
wct.setBounds(mRootTaskInfo.token, bounds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -882,7 +882,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
if (mSplitLayout == null) {
|
if (mSplitLayout == null) {
|
||||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
||||||
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
|
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
|
||||||
mDisplayImeController, mTaskOrganizer);
|
mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */);
|
||||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
|
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
|
||||||
|
|
||||||
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ public class SplitLayoutTests extends ShellTestCase {
|
|||||||
mSplitLayoutHandler,
|
mSplitLayoutHandler,
|
||||||
mCallbacks,
|
mCallbacks,
|
||||||
mDisplayImeController,
|
mDisplayImeController,
|
||||||
mTaskOrganizer));
|
mTaskOrganizer,
|
||||||
|
false /* applyDismissingParallax */));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -25,10 +25,13 @@ import android.view.SurfaceControl;
|
|||||||
import android.view.SurfaceSession;
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import androidx.test.annotation.UiThreadTest;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTestCase;
|
||||||
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
|
||||||
@@ -41,22 +44,24 @@ import org.mockito.MockitoAnnotations;
|
|||||||
/** Tests for {@link MainStage} */
|
/** Tests for {@link MainStage} */
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class MainStageTests {
|
public class MainStageTests extends ShellTestCase {
|
||||||
@Mock private ShellTaskOrganizer mTaskOrganizer;
|
@Mock private ShellTaskOrganizer mTaskOrganizer;
|
||||||
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
|
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
|
||||||
@Mock private SyncTransactionQueue mSyncQueue;
|
@Mock private SyncTransactionQueue mSyncQueue;
|
||||||
@Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
|
@Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||||
@Mock private SurfaceControl mRootLeash;
|
@Mock private SurfaceControl mRootLeash;
|
||||||
|
@Mock private IconProvider mIconProvider;
|
||||||
private WindowContainerTransaction mWct = new WindowContainerTransaction();
|
private WindowContainerTransaction mWct = new WindowContainerTransaction();
|
||||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
private MainStage mMainStage;
|
private MainStage mMainStage;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@UiThreadTest
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
|
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
|
||||||
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
|
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
|
||||||
mSurfaceSession, null);
|
mSyncQueue, mSurfaceSession, mIconProvider, null);
|
||||||
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
|
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import androidx.test.annotation.UiThreadTest;
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.ShellTestCase;
|
import com.android.wm.shell.ShellTestCase;
|
||||||
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||||
@@ -54,6 +55,7 @@ public class SideStageTests extends ShellTestCase {
|
|||||||
@Mock private SyncTransactionQueue mSyncQueue;
|
@Mock private SyncTransactionQueue mSyncQueue;
|
||||||
@Mock private ActivityManager.RunningTaskInfo mRootTask;
|
@Mock private ActivityManager.RunningTaskInfo mRootTask;
|
||||||
@Mock private SurfaceControl mRootLeash;
|
@Mock private SurfaceControl mRootLeash;
|
||||||
|
@Mock private IconProvider mIconProvider;
|
||||||
@Spy private WindowContainerTransaction mWct;
|
@Spy private WindowContainerTransaction mWct;
|
||||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
private SideStage mSideStage;
|
private SideStage mSideStage;
|
||||||
@@ -64,7 +66,7 @@ public class SideStageTests extends ShellTestCase {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
|
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
|
||||||
mSyncQueue, mSurfaceSession, null);
|
mSyncQueue, mSurfaceSession, mIconProvider, null);
|
||||||
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
|
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import androidx.test.annotation.UiThreadTest;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.ShellTestCase;
|
import com.android.wm.shell.ShellTestCase;
|
||||||
@@ -89,6 +90,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
|||||||
@Mock private Transitions mTransitions;
|
@Mock private Transitions mTransitions;
|
||||||
@Mock private SurfaceSession mSurfaceSession;
|
@Mock private SurfaceSession mSurfaceSession;
|
||||||
@Mock private SplitscreenEventLogger mLogger;
|
@Mock private SplitscreenEventLogger mLogger;
|
||||||
|
@Mock private IconProvider mIconProvider;
|
||||||
private SplitLayout mSplitLayout;
|
private SplitLayout mSplitLayout;
|
||||||
private MainStage mMainStage;
|
private MainStage mMainStage;
|
||||||
private SideStage mSideStage;
|
private SideStage mSideStage;
|
||||||
@@ -107,11 +109,13 @@ public class SplitTransitionTests extends ShellTestCase {
|
|||||||
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
|
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
|
||||||
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
|
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
|
||||||
mSplitLayout = SplitTestUtils.createMockSplitLayout();
|
mSplitLayout = SplitTestUtils.createMockSplitLayout();
|
||||||
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
|
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||||
|
mIconProvider, null);
|
||||||
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
|
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||||
|
mIconProvider, null);
|
||||||
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||||
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
|
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
|
||||||
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
|
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
|
||||||
|
|||||||
@@ -35,10 +35,13 @@ import android.view.SurfaceControl;
|
|||||||
import android.view.SurfaceSession;
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import androidx.test.annotation.UiThreadTest;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTestCase;
|
||||||
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
|
||||||
@@ -57,7 +60,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
*/
|
*/
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class StageTaskListenerTests {
|
public final class StageTaskListenerTests extends ShellTestCase {
|
||||||
private static final boolean ENABLE_SHELL_TRANSITIONS =
|
private static final boolean ENABLE_SHELL_TRANSITIONS =
|
||||||
SystemProperties.getBoolean("persist.debug.shell_transit", false);
|
SystemProperties.getBoolean("persist.debug.shell_transit", false);
|
||||||
|
|
||||||
@@ -68,6 +71,8 @@ public final class StageTaskListenerTests {
|
|||||||
@Mock
|
@Mock
|
||||||
private SyncTransactionQueue mSyncQueue;
|
private SyncTransactionQueue mSyncQueue;
|
||||||
@Mock
|
@Mock
|
||||||
|
private IconProvider mIconProvider;
|
||||||
|
@Mock
|
||||||
private StageTaskUnfoldController mStageTaskUnfoldController;
|
private StageTaskUnfoldController mStageTaskUnfoldController;
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
|
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
|
||||||
@@ -77,14 +82,17 @@ public final class StageTaskListenerTests {
|
|||||||
private StageTaskListener mStageTaskListener;
|
private StageTaskListener mStageTaskListener;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@UiThreadTest
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mStageTaskListener = new StageTaskListener(
|
mStageTaskListener = new StageTaskListener(
|
||||||
|
mContext,
|
||||||
mTaskOrganizer,
|
mTaskOrganizer,
|
||||||
DEFAULT_DISPLAY,
|
DEFAULT_DISPLAY,
|
||||||
mCallbacks,
|
mCallbacks,
|
||||||
mSyncQueue,
|
mSyncQueue,
|
||||||
mSurfaceSession,
|
mSurfaceSession,
|
||||||
|
mIconProvider,
|
||||||
mStageTaskUnfoldController);
|
mStageTaskUnfoldController);
|
||||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||||
mRootTask.parentTaskId = INVALID_TASK_ID;
|
mRootTask.parentTaskId = INVALID_TASK_ID;
|
||||||
|
|||||||
@@ -463,13 +463,13 @@ public abstract class WMShellBaseModule {
|
|||||||
@ShellMainThread ShellExecutor mainExecutor,
|
@ShellMainThread ShellExecutor mainExecutor,
|
||||||
DisplayImeController displayImeController,
|
DisplayImeController displayImeController,
|
||||||
DisplayInsetsController displayInsetsController, Transitions transitions,
|
DisplayInsetsController displayInsetsController, Transitions transitions,
|
||||||
TransactionPool transactionPool,
|
TransactionPool transactionPool, IconProvider iconProvider,
|
||||||
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
|
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
|
||||||
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
|
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
|
||||||
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
|
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
|
||||||
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
|
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
|
||||||
displayInsetsController, transitions,
|
displayInsetsController, transitions, transactionPool, iconProvider,
|
||||||
transactionPool, stageTaskUnfoldControllerProvider));
|
stageTaskUnfoldControllerProvider));
|
||||||
} else {
|
} else {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user