Merge changes from topic "staged_split_ime" into sc-dev
* changes: Adjust split layout with IME animation in split (5/N) Adjust split layout with IME animation in split (4/N) Adjust split layout with IME animation in split (3/N) Adjust split layout with IME animation in split (2/N) Adjust split layout with IME animation in split (1/N)
This commit is contained in:
@@ -16,14 +16,14 @@
|
||||
|
||||
package com.android.wm.shell;
|
||||
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
|
||||
import com.android.wm.shell.apppairs.AppPairsController;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
|
||||
import com.android.wm.shell.onehanded.OneHandedController;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -145,7 +145,7 @@ public final class ShellCommandHandlerImpl {
|
||||
}
|
||||
final int taskId = new Integer(args[2]);
|
||||
final int sideStagePosition = args.length > 3
|
||||
? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,15 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.graphics.Rect;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
@@ -35,6 +39,7 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
import com.android.wm.shell.common.SurfaceUtils;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.split.SplitLayout;
|
||||
|
||||
@@ -45,7 +50,7 @@ import java.io.PrintWriter;
|
||||
* {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
|
||||
* Also includes all UI for managing the pair like the divider.
|
||||
*/
|
||||
class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener {
|
||||
class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler {
|
||||
private static final String TAG = AppPair.class.getSimpleName();
|
||||
|
||||
private ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||
@@ -54,6 +59,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
private SurfaceControl mTaskLeash1;
|
||||
private ActivityManager.RunningTaskInfo mTaskInfo2;
|
||||
private SurfaceControl mTaskLeash2;
|
||||
private SurfaceControl mDimLayer1;
|
||||
private SurfaceControl mDimLayer2;
|
||||
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
|
||||
private final AppPairsController mController;
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
@@ -101,7 +109,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
|
||||
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
|
||||
mRootTaskInfo.configuration, this /* layoutChangeListener */,
|
||||
b -> b.setParent(mRootTaskLeash), mDisplayImeController);
|
||||
b -> b.setParent(mRootTaskLeash), mDisplayImeController,
|
||||
mController.getTaskOrganizer());
|
||||
|
||||
final WindowContainerToken token1 = task1.token;
|
||||
final WindowContainerToken token2 = task2.token;
|
||||
@@ -153,9 +162,13 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
} else if (taskInfo.taskId == getTaskId1()) {
|
||||
mTaskInfo1 = taskInfo;
|
||||
mTaskLeash1 = leash;
|
||||
mSyncQueue.runInSync(t -> mDimLayer1 =
|
||||
SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession));
|
||||
} else if (taskInfo.taskId == getTaskId2()) {
|
||||
mTaskInfo2 = taskInfo;
|
||||
mTaskLeash2 = leash;
|
||||
mSyncQueue.runInSync(t -> mDimLayer2 =
|
||||
SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession));
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
|
||||
}
|
||||
@@ -212,13 +225,32 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSplitItemPosition(WindowContainerToken token) {
|
||||
if (token == null) {
|
||||
return SPLIT_POSITION_UNDEFINED;
|
||||
}
|
||||
|
||||
if (token.equals(mTaskInfo1.getToken())) {
|
||||
return SPLIT_POSITION_TOP_OR_LEFT;
|
||||
} else if (token.equals(mTaskInfo2.getToken())) {
|
||||
return SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
}
|
||||
|
||||
return SPLIT_POSITION_UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
if (taskInfo.taskId == getRootTaskId()) {
|
||||
// We don't want to release this object back to the pool since the root task went away.
|
||||
mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
|
||||
} else if (taskInfo.taskId == getTaskId1() || taskInfo.taskId == getTaskId2()) {
|
||||
} else if (taskInfo.taskId == getTaskId1()) {
|
||||
mController.unpair(mRootTaskInfo.taskId);
|
||||
mSyncQueue.runInSync(t -> t.remove(mDimLayer1));
|
||||
} else if (taskInfo.taskId == getTaskId2()) {
|
||||
mController.unpair(mRootTaskInfo.taskId);
|
||||
mSyncQueue.runInSync(t -> t.remove(mDimLayer2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,40 +296,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
|
||||
@Override
|
||||
public void onBoundsChanging(SplitLayout layout) {
|
||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
||||
if (dividerLeash == null) return;
|
||||
final Rect dividerBounds = layout.getDividerBounds();
|
||||
final Rect bounds1 = layout.getBounds1();
|
||||
final Rect bounds2 = layout.getBounds2();
|
||||
mSyncQueue.runInSync(t -> t
|
||||
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
|
||||
.setPosition(mTaskLeash1, bounds1.left, bounds1.top)
|
||||
.setPosition(mTaskLeash2, bounds2.left, bounds2.top)
|
||||
// Sets crop to prevent visible region of tasks overlap with each other when
|
||||
// re-positioning surfaces while resizing.
|
||||
.setWindowCrop(mTaskLeash1, bounds1.width(), bounds1.height())
|
||||
.setWindowCrop(mTaskLeash2, bounds2.width(), bounds2.height()));
|
||||
mSyncQueue.runInSync(t ->
|
||||
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBoundsChanged(SplitLayout layout) {
|
||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
||||
if (dividerLeash == null) return;
|
||||
final Rect dividerBounds = layout.getDividerBounds();
|
||||
final Rect bounds1 = layout.getBounds1();
|
||||
final Rect bounds2 = layout.getBounds2();
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.setBounds(mTaskInfo1.token, bounds1)
|
||||
.setBounds(mTaskInfo2.token, bounds2);
|
||||
mController.getTaskOrganizer().applyTransaction(wct);
|
||||
mSyncQueue.runInSync(t -> t
|
||||
// Resets layer of divider bar to make sure it is always on top.
|
||||
.setLayer(dividerLeash, Integer.MAX_VALUE)
|
||||
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
|
||||
.setPosition(mTaskLeash1, bounds1.left, bounds1.top)
|
||||
.setPosition(mTaskLeash2, bounds2.left, bounds2.top)
|
||||
// Resets crop to apply new surface bounds directly.
|
||||
.setWindowCrop(mTaskLeash1, null)
|
||||
.setWindowCrop(mTaskLeash2, null));
|
||||
layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t ->
|
||||
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
|
||||
/**
|
||||
* Helpers for handling surface.
|
||||
*/
|
||||
public class SurfaceUtils {
|
||||
/** Creates a dim layer above indicated 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();
|
||||
t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
|
||||
return dimLayer;
|
||||
}
|
||||
}
|
||||
@@ -36,13 +36,11 @@ import androidx.annotation.Nullable;
|
||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.animation.Interpolators;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
|
||||
/**
|
||||
* Divider for multi window splits.
|
||||
*/
|
||||
public class DividerView extends FrameLayout implements View.OnTouchListener,
|
||||
DisplayImeController.ImePositionProcessor {
|
||||
public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
public static final long TOUCH_ANIMATION_DURATION = 150;
|
||||
public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
|
||||
|
||||
@@ -98,12 +96,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
|
||||
setOnTouchListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImeVisibilityChanged(int displayId, boolean isShowing) {
|
||||
if (displayId != getDisplay().getDisplayId()) return;
|
||||
setInteractive(!isShowing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (mSplitLayout == null || !mInteractive) {
|
||||
@@ -217,7 +209,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
|
||||
mViewHost.relayout(lp);
|
||||
}
|
||||
|
||||
private void setInteractive(boolean interactive) {
|
||||
void setInteractive(boolean interactive) {
|
||||
if (interactive == mInteractive) return;
|
||||
mInteractive = interactive;
|
||||
releaseTouching();
|
||||
|
||||
@@ -25,16 +25,22 @@ import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_D
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.IntDef;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.animation.Interpolators;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
|
||||
@@ -42,7 +48,32 @@ import com.android.wm.shell.common.DisplayImeController;
|
||||
* Records and handles layout of splits. Helps to calculate proper bounds when configuration or
|
||||
* divide position changes.
|
||||
*/
|
||||
public class SplitLayout {
|
||||
public final class SplitLayout {
|
||||
/**
|
||||
* Split position isn't specified normally meaning to use what ever it is currently set to.
|
||||
*/
|
||||
public static final int SPLIT_POSITION_UNDEFINED = -1;
|
||||
|
||||
/**
|
||||
* Specifies that a split is positioned at the top half of the screen if
|
||||
* in portrait mode or at the left half of the screen if in landscape mode.
|
||||
*/
|
||||
public static final int SPLIT_POSITION_TOP_OR_LEFT = 0;
|
||||
|
||||
/**
|
||||
* Specifies that a split is positioned at the bottom half of the screen if
|
||||
* in portrait mode or at the right half of the screen if in landscape mode.
|
||||
*/
|
||||
public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
|
||||
|
||||
@IntDef(prefix = {"SPLIT_POSITION_"}, value = {
|
||||
SPLIT_POSITION_UNDEFINED,
|
||||
SPLIT_POSITION_TOP_OR_LEFT,
|
||||
SPLIT_POSITION_BOTTOM_OR_RIGHT
|
||||
})
|
||||
public @interface SplitPosition {
|
||||
}
|
||||
|
||||
private final int mDividerWindowWidth;
|
||||
private final int mDividerInsets;
|
||||
private final int mDividerSize;
|
||||
@@ -51,8 +82,11 @@ public class SplitLayout {
|
||||
private final Rect mDividerBounds = new Rect();
|
||||
private final Rect mBounds1 = new Rect();
|
||||
private final Rect mBounds2 = new Rect();
|
||||
private final LayoutChangeListener mLayoutChangeListener;
|
||||
private final SplitLayoutHandler mSplitLayoutHandler;
|
||||
private final SplitWindowManager mSplitWindowManager;
|
||||
private final DisplayImeController mDisplayImeController;
|
||||
private final ImePositionProcessor mImePositionProcessor;
|
||||
private final ShellTaskOrganizer mTaskOrganizer;
|
||||
|
||||
private Context mContext;
|
||||
private DividerSnapAlgorithm mDividerSnapAlgorithm;
|
||||
@@ -60,18 +94,21 @@ public class SplitLayout {
|
||||
private boolean mInitialized = false;
|
||||
|
||||
public SplitLayout(String windowName, Context context, Configuration configuration,
|
||||
LayoutChangeListener layoutChangeListener,
|
||||
SplitLayoutHandler splitLayoutHandler,
|
||||
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
|
||||
DisplayImeController displayImeController) {
|
||||
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
|
||||
mContext = context.createConfigurationContext(configuration);
|
||||
mLayoutChangeListener = layoutChangeListener;
|
||||
mSplitLayoutHandler = splitLayoutHandler;
|
||||
mDisplayImeController = displayImeController;
|
||||
mSplitWindowManager = new SplitWindowManager(
|
||||
windowName, mContext, configuration, parentContainerCallbacks,
|
||||
displayImeController);
|
||||
windowName, mContext, configuration, parentContainerCallbacks);
|
||||
mTaskOrganizer = taskOrganizer;
|
||||
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
|
||||
|
||||
mDividerWindowWidth = context.getResources().getDimensionPixelSize(
|
||||
final Resources resources = context.getResources();
|
||||
mDividerWindowWidth = resources.getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.docked_stack_divider_thickness);
|
||||
mDividerInsets = context.getResources().getDimensionPixelSize(
|
||||
mDividerInsets = resources.getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.docked_stack_divider_insets);
|
||||
mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
|
||||
|
||||
@@ -82,17 +119,17 @@ public class SplitLayout {
|
||||
|
||||
/** Gets bounds of the primary split. */
|
||||
public Rect getBounds1() {
|
||||
return mBounds1;
|
||||
return new Rect(mBounds1);
|
||||
}
|
||||
|
||||
/** Gets bounds of the secondary split. */
|
||||
public Rect getBounds2() {
|
||||
return mBounds2;
|
||||
return new Rect(mBounds2);
|
||||
}
|
||||
|
||||
/** Gets bounds of divider window. */
|
||||
public Rect getDividerBounds() {
|
||||
return mDividerBounds;
|
||||
return new Rect(mDividerBounds);
|
||||
}
|
||||
|
||||
/** Returns leash of the current divider bar. */
|
||||
@@ -153,6 +190,7 @@ public class SplitLayout {
|
||||
if (mInitialized) return;
|
||||
mInitialized = true;
|
||||
mSplitWindowManager.init(this);
|
||||
mDisplayImeController.addPositionProcessor(mImePositionProcessor);
|
||||
}
|
||||
|
||||
/** Releases the surface holding the current {@link DividerView}. */
|
||||
@@ -160,6 +198,8 @@ public class SplitLayout {
|
||||
if (!mInitialized) return;
|
||||
mInitialized = false;
|
||||
mSplitWindowManager.release();
|
||||
mDisplayImeController.removePositionProcessor(mImePositionProcessor);
|
||||
mImePositionProcessor.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,14 +208,14 @@ public class SplitLayout {
|
||||
*/
|
||||
void updateDivideBounds(int position) {
|
||||
updateBounds(position);
|
||||
mLayoutChangeListener.onBoundsChanging(this);
|
||||
mSplitWindowManager.setResizingSplits(true);
|
||||
mSplitLayoutHandler.onBoundsChanging(this);
|
||||
}
|
||||
|
||||
void setDividePosition(int position) {
|
||||
mDividePosition = position;
|
||||
updateBounds(mDividePosition);
|
||||
mLayoutChangeListener.onBoundsChanged(this);
|
||||
mSplitLayoutHandler.onBoundsChanged(this);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
}
|
||||
|
||||
@@ -192,11 +232,11 @@ public class SplitLayout {
|
||||
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
|
||||
switch (snapTarget.flag) {
|
||||
case FLAG_DISMISS_START:
|
||||
mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */);
|
||||
mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
break;
|
||||
case FLAG_DISMISS_END:
|
||||
mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */);
|
||||
mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
break;
|
||||
default:
|
||||
@@ -206,7 +246,7 @@ public class SplitLayout {
|
||||
}
|
||||
|
||||
void onDoubleTappedDivider() {
|
||||
mLayoutChangeListener.onDoubleTappedDivider();
|
||||
mSplitLayoutHandler.onDoubleTappedDivider();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,8 +305,38 @@ public class SplitLayout {
|
||||
return bounds.width() > bounds.height();
|
||||
}
|
||||
|
||||
/** Listens layout change event. */
|
||||
public interface LayoutChangeListener {
|
||||
/** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
|
||||
public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
|
||||
SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
|
||||
final Rect dividerBounds = mImePositionProcessor.adjustForIme(mDividerBounds);
|
||||
final Rect bounds1 = mImePositionProcessor.adjustForIme(mBounds1);
|
||||
final Rect bounds2 = mImePositionProcessor.adjustForIme(mBounds2);
|
||||
final SurfaceControl dividerLeash = getDividerLeash();
|
||||
if (dividerLeash != null) {
|
||||
t.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
|
||||
// Resets layer of divider bar to make sure it is always on top.
|
||||
.setLayer(dividerLeash, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
t.setPosition(leash1, bounds1.left, bounds1.top)
|
||||
.setWindowCrop(leash1, bounds1.width(), bounds1.height());
|
||||
|
||||
t.setPosition(leash2, bounds2.left, bounds2.top)
|
||||
.setWindowCrop(leash2, bounds2.width(), bounds2.height());
|
||||
|
||||
mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2);
|
||||
}
|
||||
|
||||
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
|
||||
public void applyTaskChanges(WindowContainerTransaction wct,
|
||||
ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
|
||||
wct.setBounds(task1.token, mImePositionProcessor.adjustForIme(mBounds1))
|
||||
.setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2));
|
||||
}
|
||||
|
||||
/** Handles layout change event. */
|
||||
public interface SplitLayoutHandler {
|
||||
|
||||
/** Calls when dismissing split. */
|
||||
void onSnappedToDismiss(boolean snappedToEnd);
|
||||
|
||||
@@ -279,5 +349,133 @@ public class SplitLayout {
|
||||
/** Calls when user double tapped on the divider bar. */
|
||||
default void onDoubleTappedDivider() {
|
||||
}
|
||||
|
||||
/** Returns split position of the token. */
|
||||
@SplitPosition
|
||||
int getSplitItemPosition(WindowContainerToken token);
|
||||
}
|
||||
|
||||
/** Records IME top offset changes and updates SplitLayout correspondingly. */
|
||||
private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
|
||||
/**
|
||||
* Maximum size of an adjusted split bounds relative to original stack bounds. Used to
|
||||
* restrict IME adjustment so that a min portion of top split remains visible.
|
||||
*/
|
||||
private static final float ADJUSTED_SPLIT_FRACTION_MAX = 0.7f;
|
||||
private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
|
||||
|
||||
private final int mDisplayId;
|
||||
|
||||
private int mYOffsetForIme;
|
||||
private float mDimValue1;
|
||||
private float mDimValue2;
|
||||
|
||||
private int mStartImeTop;
|
||||
private int mEndImeTop;
|
||||
|
||||
private int mTargetYOffset;
|
||||
private int mLastYOffset;
|
||||
private float mTargetDim1;
|
||||
private float mTargetDim2;
|
||||
private float mLastDim1;
|
||||
private float mLastDim2;
|
||||
|
||||
private ImePositionProcessor(int displayId) {
|
||||
mDisplayId = displayId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
|
||||
boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
|
||||
if (displayId != mDisplayId) return 0;
|
||||
final int imeTargetPosition = getImeTargetPosition();
|
||||
if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0;
|
||||
mStartImeTop = showing ? hiddenTop : shownTop;
|
||||
mEndImeTop = showing ? shownTop : hiddenTop;
|
||||
|
||||
// Update target dim values
|
||||
mLastDim1 = mDimValue1;
|
||||
mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && showing
|
||||
? ADJUSTED_NONFOCUS_DIM : 0.0f;
|
||||
mLastDim2 = mDimValue2;
|
||||
mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && showing
|
||||
? ADJUSTED_NONFOCUS_DIM : 0.0f;
|
||||
|
||||
// Calculate target bounds offset for IME
|
||||
mLastYOffset = mYOffsetForIme;
|
||||
final boolean needOffset = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
|
||||
&& !isFloating && !isLandscape(mRootBounds) && showing;
|
||||
mTargetYOffset = needOffset ? getTargetYOffset() : 0;
|
||||
|
||||
// Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
|
||||
// ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
|
||||
// because DividerView won't receive onImeVisibilityChanged callback after it being
|
||||
// re-inflated.
|
||||
mSplitWindowManager.setInteractive(
|
||||
!showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
|
||||
if (displayId != mDisplayId) return;
|
||||
onProgress(getProgress(imeTop));
|
||||
mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImeEndPositioning(int displayId, boolean cancel,
|
||||
SurfaceControl.Transaction t) {
|
||||
if (displayId != mDisplayId || cancel) return;
|
||||
onProgress(1.0f);
|
||||
mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
|
||||
}
|
||||
|
||||
private int getTargetYOffset() {
|
||||
final int desireOffset = Math.abs(mEndImeTop - mStartImeTop);
|
||||
// Make sure to keep at least 30% visible for the top split.
|
||||
final int maxOffset = (int) (mBounds1.height() * ADJUSTED_SPLIT_FRACTION_MAX);
|
||||
return -Math.min(desireOffset, maxOffset);
|
||||
}
|
||||
|
||||
@SplitPosition
|
||||
private int getImeTargetPosition() {
|
||||
final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId);
|
||||
return mSplitLayoutHandler.getSplitItemPosition(token);
|
||||
}
|
||||
|
||||
private float getProgress(int currImeTop) {
|
||||
return ((float) currImeTop - mStartImeTop) / (mEndImeTop - mStartImeTop);
|
||||
}
|
||||
|
||||
private void onProgress(float progress) {
|
||||
mDimValue1 = getProgressValue(mLastDim1, mTargetDim1, progress);
|
||||
mDimValue2 = getProgressValue(mLastDim2, mTargetDim2, progress);
|
||||
mYOffsetForIme =
|
||||
(int) getProgressValue((float) mLastYOffset, (float) mTargetYOffset, progress);
|
||||
}
|
||||
|
||||
private float getProgressValue(float start, float end, float progress) {
|
||||
return start + (end - start) * progress;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
mYOffsetForIme = 0;
|
||||
mDimValue1 = mDimValue2 = 0.0f;
|
||||
}
|
||||
|
||||
/* Adjust bounds with IME offset. */
|
||||
private Rect adjustForIme(Rect bounds) {
|
||||
final Rect temp = new Rect(bounds);
|
||||
if (mYOffsetForIme != 0) temp.offset(0, mYOffsetForIme);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1,
|
||||
SurfaceControl dimLayer2) {
|
||||
t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
|
||||
t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ import android.view.WindowlessWindowManager;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
|
||||
/**
|
||||
* Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split.
|
||||
@@ -55,7 +54,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
private static final String TAG = SplitWindowManager.class.getSimpleName();
|
||||
|
||||
private final String mWindowName;
|
||||
private final DisplayImeController mDisplayImeController;
|
||||
private final ParentContainerCallbacks mParentContainerCallbacks;
|
||||
private Context mContext;
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
@@ -68,13 +66,11 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
}
|
||||
|
||||
public SplitWindowManager(String windowName, Context context, Configuration config,
|
||||
ParentContainerCallbacks parentContainerCallbacks,
|
||||
DisplayImeController displayImeController) {
|
||||
ParentContainerCallbacks parentContainerCallbacks) {
|
||||
super(config, null /* rootSurface */, null /* hostInputToken */);
|
||||
mContext = context.createConfigurationContext(config);
|
||||
mParentContainerCallbacks = parentContainerCallbacks;
|
||||
mWindowName = windowName;
|
||||
mDisplayImeController = displayImeController;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,7 +124,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
mViewHost.setView(mDividerView, lp);
|
||||
mDividerView.setup(splitLayout, mViewHost);
|
||||
mDisplayImeController.addPositionProcessor(mDividerView);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +132,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
*/
|
||||
void release() {
|
||||
if (mDividerView != null) {
|
||||
mDisplayImeController.removePositionProcessor(mDividerView);
|
||||
mDividerView = null;
|
||||
}
|
||||
|
||||
@@ -152,6 +146,11 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setInteractive(boolean interactive) {
|
||||
if (mDividerView == null) return;
|
||||
mDividerView.setInteractive(interactive);
|
||||
}
|
||||
|
||||
void setResizingSplits(boolean resizing) {
|
||||
if (resizing == mResizingSplits) return;
|
||||
try {
|
||||
|
||||
@@ -29,14 +29,14 @@ import static android.content.Intent.EXTRA_SHORTCUT_ID;
|
||||
import static android.content.Intent.EXTRA_TASK_ID;
|
||||
import static android.content.Intent.EXTRA_USER;
|
||||
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
|
||||
@@ -64,7 +64,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen.StagePosition;
|
||||
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
|
||||
@@ -203,10 +203,10 @@ public class DragAndDropPolicy {
|
||||
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
|
||||
|
||||
@StageType int stage = STAGE_TYPE_UNDEFINED;
|
||||
@StagePosition int position = STAGE_POSITION_UNDEFINED;
|
||||
@SplitPosition int position = SPLIT_POSITION_UNDEFINED;
|
||||
if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
|
||||
// Update launch options for the split side we are targeting.
|
||||
position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
position = leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
if (!inSplitScreen) {
|
||||
// Launch in the side stage if we are not in split-screen already.
|
||||
stage = STAGE_TYPE_SIDE;
|
||||
@@ -219,7 +219,7 @@ public class DragAndDropPolicy {
|
||||
}
|
||||
|
||||
private void startClipDescription(ClipDescription description, Intent intent,
|
||||
@StageType int stage, @StagePosition int position) {
|
||||
@StageType int stage, @SplitPosition int position) {
|
||||
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
|
||||
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
|
||||
final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
|
||||
@@ -291,12 +291,12 @@ public class DragAndDropPolicy {
|
||||
* Interface for actually committing the task launches.
|
||||
*/
|
||||
public interface Starter {
|
||||
void startTask(int taskId, @StageType int stage, @StagePosition int position,
|
||||
void startTask(int taskId, @StageType int stage, @SplitPosition int position,
|
||||
@Nullable Bundle options);
|
||||
void startShortcut(String packageName, String shortcutId, @StageType int stage,
|
||||
@StagePosition int position, @Nullable Bundle options, UserHandle user);
|
||||
@SplitPosition int position, @Nullable Bundle options, UserHandle user);
|
||||
void startIntent(PendingIntent intent, Intent fillInIntent,
|
||||
@StageType int stage, @StagePosition int position,
|
||||
@StageType int stage, @SplitPosition int position,
|
||||
@Nullable Bundle options);
|
||||
void enterSplitScreen(int taskId, boolean leftOrTop);
|
||||
void exitSplitScreen();
|
||||
|
||||
@@ -577,7 +577,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
|
||||
mSplits.getSplitTransitions().dismissSplit(
|
||||
mSplits, mSplitLayout, !toPrimaryTask, snapped);
|
||||
} else {
|
||||
mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask);
|
||||
mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask);
|
||||
onDismissSplit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
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.transition.Transitions;
|
||||
|
||||
@@ -70,9 +71,9 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
private final LegacySplitScreenTransitions mSplitTransitions;
|
||||
|
||||
LegacySplitScreenTaskListener(LegacySplitScreenController splitScreenController,
|
||||
ShellTaskOrganizer shellTaskOrganizer,
|
||||
Transitions transitions,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
ShellTaskOrganizer shellTaskOrganizer,
|
||||
Transitions transitions,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
mSplitScreenController = splitScreenController;
|
||||
mTaskOrganizer = shellTaskOrganizer;
|
||||
mSplitTransitions = new LegacySplitScreenTransitions(splitScreenController.mTransactionPool,
|
||||
@@ -146,21 +147,11 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG);
|
||||
|
||||
// Initialize dim surfaces:
|
||||
mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
|
||||
.setParent(mPrimarySurface).setColorLayer()
|
||||
.setName("Primary Divider Dim")
|
||||
.setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
|
||||
.build();
|
||||
mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
|
||||
.setParent(mSecondarySurface).setColorLayer()
|
||||
.setName("Secondary Divider Dim")
|
||||
.setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
|
||||
.build();
|
||||
SurfaceControl.Transaction t = getTransaction();
|
||||
t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
|
||||
t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
|
||||
t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
|
||||
t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
|
||||
mPrimaryDim = SurfaceUtils.makeDimLayer(
|
||||
t, mPrimarySurface, "Primary Divider Dim", mSurfaceSession);
|
||||
mSecondaryDim = SurfaceUtils.makeDimLayer(
|
||||
t, mSecondarySurface, "Secondary Divider Dim", mSurfaceSession);
|
||||
t.apply();
|
||||
releaseTransaction(t);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.wm.shell.splitscreen;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
@@ -36,8 +37,9 @@ class MainStage extends StageTaskListener {
|
||||
private boolean mIsActive = false;
|
||||
|
||||
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
|
||||
super(taskOrganizer, displayId, callbacks, syncQueue);
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession) {
|
||||
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
|
||||
}
|
||||
|
||||
boolean isActive() {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.graphics.Rect;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
@@ -33,8 +34,9 @@ class SideStage extends StageTaskListener {
|
||||
private static final String TAG = SideStage.class.getSimpleName();
|
||||
|
||||
SideStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
|
||||
super(taskOrganizer, displayId, callbacks, syncQueue);
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession) {
|
||||
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
|
||||
}
|
||||
|
||||
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.wm.shell.splitscreen;
|
||||
import android.annotation.IntDef;
|
||||
|
||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
|
||||
|
||||
/**
|
||||
* Interface to engage split-screen feature.
|
||||
@@ -26,29 +27,6 @@ import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
*/
|
||||
@ExternalThread
|
||||
public interface SplitScreen {
|
||||
/**
|
||||
* Stage position isn't specified normally meaning to use what ever it is currently set to.
|
||||
*/
|
||||
int STAGE_POSITION_UNDEFINED = -1;
|
||||
/**
|
||||
* Specifies that a stage is positioned at the top half of the screen if
|
||||
* in portrait mode or at the left half of the screen if in landscape mode.
|
||||
*/
|
||||
int STAGE_POSITION_TOP_OR_LEFT = 0;
|
||||
|
||||
/**
|
||||
* Specifies that a stage is positioned at the bottom half of the screen if
|
||||
* in portrait mode or at the right half of the screen if in landscape mode.
|
||||
*/
|
||||
int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
|
||||
|
||||
@IntDef(prefix = { "STAGE_POSITION_" }, value = {
|
||||
STAGE_POSITION_UNDEFINED,
|
||||
STAGE_POSITION_TOP_OR_LEFT,
|
||||
STAGE_POSITION_BOTTOM_OR_RIGHT
|
||||
})
|
||||
@interface StagePosition {}
|
||||
|
||||
/**
|
||||
* Stage type isn't specified normally meaning to use what ever the default is.
|
||||
* E.g. exit split-screen and launch the app in fullscreen.
|
||||
@@ -75,7 +53,7 @@ public interface SplitScreen {
|
||||
|
||||
/** Callback interface for listening to changes in a split-screen stage. */
|
||||
interface SplitScreenListener {
|
||||
void onStagePositionChanged(@StageType int stage, @StagePosition int position);
|
||||
void onStagePositionChanged(@StageType int stage, @SplitPosition int position);
|
||||
void onTaskStageChanged(int taskId, @StageType int stage, boolean visible);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ package com.android.wm.shell.splitscreen;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
@@ -53,6 +53,7 @@ import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
|
||||
import com.android.wm.shell.splitscreen.ISplitScreenListener;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
@@ -122,7 +123,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
return mStageCoordinator.isSplitScreenVisible();
|
||||
}
|
||||
|
||||
public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) {
|
||||
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
|
||||
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
|
||||
if (task == null) {
|
||||
throw new IllegalArgumentException("Unknown taskId" + taskId);
|
||||
@@ -131,7 +132,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||
@SplitScreen.StagePosition int sideStagePosition) {
|
||||
@SplitPosition int sideStagePosition) {
|
||||
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
return mStageCoordinator.removeFromSideStage(taskId);
|
||||
}
|
||||
|
||||
public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
|
||||
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
|
||||
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
||||
}
|
||||
|
||||
@@ -149,7 +150,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
|
||||
public void enterSplitScreen(int taskId, boolean leftOrTop) {
|
||||
moveToSideStage(taskId,
|
||||
leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
|
||||
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
||||
}
|
||||
|
||||
public void exitSplitScreen() {
|
||||
@@ -173,7 +174,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
public void startTask(int taskId, @SplitScreen.StageType int stage,
|
||||
@SplitScreen.StagePosition int position, @Nullable Bundle options) {
|
||||
@SplitPosition int position, @Nullable Bundle options) {
|
||||
options = resolveStartStage(stage, position, options);
|
||||
|
||||
try {
|
||||
@@ -184,7 +185,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
public void startShortcut(String packageName, String shortcutId,
|
||||
@SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
|
||||
@SplitScreen.StageType int stage, @SplitPosition int position,
|
||||
@Nullable Bundle options, UserHandle user) {
|
||||
options = resolveStartStage(stage, position, options);
|
||||
|
||||
@@ -199,7 +200,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
public void startIntent(PendingIntent intent, Intent fillInIntent,
|
||||
@SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
|
||||
@SplitScreen.StageType int stage, @SplitPosition int position,
|
||||
@Nullable Bundle options) {
|
||||
options = resolveStartStage(stage, position, options);
|
||||
|
||||
@@ -211,11 +212,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
private Bundle resolveStartStage(@SplitScreen.StageType int stage,
|
||||
@SplitScreen.StagePosition int position, @Nullable Bundle options) {
|
||||
@SplitPosition int position, @Nullable Bundle options) {
|
||||
switch (stage) {
|
||||
case STAGE_TYPE_UNDEFINED: {
|
||||
// Use the stage of the specified position is valid.
|
||||
if (position != STAGE_POSITION_UNDEFINED) {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
if (position == mStageCoordinator.getSideStagePosition()) {
|
||||
options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
|
||||
} else {
|
||||
@@ -228,7 +229,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
break;
|
||||
}
|
||||
case STAGE_TYPE_SIDE: {
|
||||
if (position != STAGE_POSITION_UNDEFINED) {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
mStageCoordinator.setSideStagePosition(position);
|
||||
} else {
|
||||
position = mStageCoordinator.getSideStagePosition();
|
||||
@@ -240,10 +241,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
break;
|
||||
}
|
||||
case STAGE_TYPE_MAIN: {
|
||||
if (position != STAGE_POSITION_UNDEFINED) {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
// Set the side stage opposite of what we want to the main stage.
|
||||
final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT
|
||||
? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
|
||||
final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
||||
} else {
|
||||
position = mStageCoordinator.getMainStagePosition();
|
||||
@@ -418,7 +419,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
@Override
|
||||
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
|
||||
int sideTaskId, @Nullable Bundle sideOptions,
|
||||
@SplitScreen.StagePosition int sidePosition,
|
||||
@SplitPosition int sidePosition,
|
||||
@Nullable IRemoteTransition remoteTransition) {
|
||||
executeRemoteCallWithTaskPermission(mController, "startTasks",
|
||||
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
|
||||
|
||||
@@ -25,8 +25,9 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.view.WindowManager.transitTypeToString;
|
||||
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
@@ -47,14 +48,15 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.WindowManager;
|
||||
import android.window.DisplayAreaInfo;
|
||||
import android.window.IRemoteTransition;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
@@ -64,6 +66,7 @@ import com.android.wm.shell.common.DisplayImeController;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.split.SplitLayout;
|
||||
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
@@ -84,19 +87,22 @@ import java.util.List;
|
||||
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
|
||||
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
|
||||
*/
|
||||
class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
|
||||
class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
|
||||
|
||||
private static final String TAG = StageCoordinator.class.getSimpleName();
|
||||
|
||||
/** internal value for mDismissTop that represents no dismiss */
|
||||
private static final int NO_DISMISS = -2;
|
||||
|
||||
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
|
||||
private final MainStage mMainStage;
|
||||
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
|
||||
private final SideStage mSideStage;
|
||||
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
|
||||
private @SplitScreen.StagePosition int mSideStagePosition = STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
@SplitPosition
|
||||
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
|
||||
private final int mDisplayId;
|
||||
private SplitLayout mSplitLayout;
|
||||
@@ -115,8 +121,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
/** Whether the device is supporting legacy split or not. */
|
||||
private boolean mUseLegacySplit;
|
||||
|
||||
|
||||
@SplitScreen.StageType int mDismissTop = NO_DISMISS;
|
||||
|
||||
private final Runnable mOnTransitionAnimationComplete = () -> {
|
||||
// If still playing, let it finish.
|
||||
if (!isSplitScreenVisible()) {
|
||||
@@ -137,8 +143,18 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
mSyncQueue = syncQueue;
|
||||
mRootTDAOrganizer = rootTDAOrganizer;
|
||||
mTaskOrganizer = taskOrganizer;
|
||||
mMainStage = new MainStage(mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue);
|
||||
mSideStage = new SideStage(mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue);
|
||||
mMainStage = new MainStage(
|
||||
mTaskOrganizer,
|
||||
mDisplayId,
|
||||
mMainStageListener,
|
||||
mSyncQueue,
|
||||
mSurfaceSession);
|
||||
mSideStage = new SideStage(
|
||||
mTaskOrganizer,
|
||||
mDisplayId,
|
||||
mSideStageListener,
|
||||
mSyncQueue,
|
||||
mSurfaceSession);
|
||||
mDisplayImeController = displayImeController;
|
||||
mRootTDAOrganizer.registerListener(displayId, this);
|
||||
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
|
||||
@@ -176,7 +192,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
}
|
||||
|
||||
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||
@SplitScreen.StagePosition int sideStagePosition) {
|
||||
@SplitPosition int sideStagePosition) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
setSideStagePosition(sideStagePosition);
|
||||
mMainStage.activate(getMainStageBounds(), wct);
|
||||
@@ -201,7 +217,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
|
||||
/** Starts 2 tasks in one transition. */
|
||||
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
|
||||
@Nullable Bundle sideOptions, @SplitScreen.StagePosition int sidePosition,
|
||||
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
|
||||
@Nullable IRemoteTransition remoteTransition) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
mainOptions = mainOptions != null ? mainOptions : new Bundle();
|
||||
@@ -225,20 +241,22 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
|
||||
}
|
||||
|
||||
@SplitScreen.StagePosition int getSideStagePosition() {
|
||||
@SplitLayout.SplitPosition
|
||||
int getSideStagePosition() {
|
||||
return mSideStagePosition;
|
||||
}
|
||||
|
||||
@SplitScreen.StagePosition int getMainStagePosition() {
|
||||
return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
||||
? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
|
||||
@SplitLayout.SplitPosition
|
||||
int getMainStagePosition() {
|
||||
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||
}
|
||||
|
||||
void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
|
||||
void setSideStagePosition(@SplitPosition int sideStagePosition) {
|
||||
setSideStagePosition(sideStagePosition, true /* updateVisibility */);
|
||||
}
|
||||
|
||||
private void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition,
|
||||
private void setSideStagePosition(@SplitPosition int sideStagePosition,
|
||||
boolean updateVisibility) {
|
||||
if (mSideStagePosition == sideStagePosition) return;
|
||||
mSideStagePosition = sideStagePosition;
|
||||
@@ -289,7 +307,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
|
||||
}
|
||||
|
||||
void updateActivityOptions(Bundle opts, @SplitScreen.StagePosition int position) {
|
||||
void updateActivityOptions(Bundle opts, @SplitPosition int position) {
|
||||
addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
|
||||
|
||||
if (!mMainStage.isActive()) {
|
||||
@@ -487,8 +505,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
@Override
|
||||
public void onSnappedToDismiss(boolean bottomOrRight) {
|
||||
final boolean mainStageToTop =
|
||||
bottomOrRight ? mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT
|
||||
: mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT;
|
||||
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
|
||||
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
onSnappedToDismissTransition(mainStageToTop);
|
||||
return;
|
||||
@@ -497,55 +515,49 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBoundsChanging(SplitLayout layout) {
|
||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
||||
if (dividerLeash == null) return;
|
||||
final Rect mainStageBounds = getMainStageBounds();
|
||||
final Rect sideStageBounds = getSideStageBounds();
|
||||
|
||||
mSyncQueue.runInSync(t -> t
|
||||
.setPosition(dividerLeash,
|
||||
mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)
|
||||
.setPosition(mMainStage.mRootLeash, mainStageBounds.left, mainStageBounds.top)
|
||||
.setPosition(mSideStage.mRootLeash, sideStageBounds.left, sideStageBounds.top)
|
||||
// Sets crop to prevent visible region of tasks overlap with each other when
|
||||
// re-positioning surfaces while resizing.
|
||||
.setWindowCrop(mMainStage.mRootLeash,
|
||||
mainStageBounds.width(), mainStageBounds.height())
|
||||
.setWindowCrop(mSideStage.mRootLeash,
|
||||
sideStageBounds.width(), sideStageBounds.height()));
|
||||
|
||||
public void onDoubleTappedDivider() {
|
||||
setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDoubleTappedDivider() {
|
||||
setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
||||
? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT);
|
||||
public void onBoundsChanging(SplitLayout layout) {
|
||||
final StageTaskListener topLeftStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
||||
final StageTaskListener bottomRightStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
|
||||
|
||||
mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
|
||||
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBoundsChanged(SplitLayout layout) {
|
||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
||||
if (dividerLeash == null) return;
|
||||
final Rect mainStageBounds = getMainStageBounds();
|
||||
final Rect sideStageBounds = getSideStageBounds();
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
mMainStage.setBounds(mainStageBounds, wct);
|
||||
mSideStage.setBounds(sideStageBounds, wct);
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
final StageTaskListener topLeftStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
||||
final StageTaskListener bottomRightStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
|
||||
|
||||
mSyncQueue.runInSync(t -> t
|
||||
// Resets layer of divider bar to make sure it is always on top.
|
||||
.setLayer(dividerLeash, Integer.MAX_VALUE)
|
||||
.setPosition(dividerLeash,
|
||||
mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)
|
||||
.setPosition(mMainStage.mRootLeash,
|
||||
mainStageBounds.left, mainStageBounds.top)
|
||||
.setPosition(mSideStage.mRootLeash,
|
||||
sideStageBounds.left, sideStageBounds.top)
|
||||
// Resets crop to apply new surface bounds directly.
|
||||
.setWindowCrop(mMainStage.mRootLeash, null)
|
||||
.setWindowCrop(mSideStage.mRootLeash, null));
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
|
||||
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSplitItemPosition(WindowContainerToken token) {
|
||||
if (token == null) {
|
||||
return SPLIT_POSITION_UNDEFINED;
|
||||
}
|
||||
|
||||
if (token.equals(mMainStage.mRootTaskInfo.getToken())) {
|
||||
return getMainStagePosition();
|
||||
} else if (token.equals(mSideStage.mRootTaskInfo.getToken())) {
|
||||
return getSideStagePosition();
|
||||
}
|
||||
|
||||
return SPLIT_POSITION_UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -555,7 +567,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
||||
mDisplayAreaInfo.configuration, this,
|
||||
b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
|
||||
mDisplayImeController);
|
||||
mDisplayImeController, mTaskOrganizer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,12 +586,12 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
}
|
||||
|
||||
private Rect getSideStageBounds() {
|
||||
return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
||||
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
|
||||
}
|
||||
|
||||
private Rect getMainStageBounds() {
|
||||
return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
||||
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
|
||||
}
|
||||
|
||||
@@ -742,7 +754,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
|
||||
// Update local states (before animating).
|
||||
setDividerVisibility(true);
|
||||
setSideStagePosition(STAGE_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
|
||||
setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
|
||||
setSplitsVisible(true);
|
||||
|
||||
addDividerBarToTransition(info, t, true /* show */);
|
||||
|
||||
@@ -29,11 +29,13 @@ import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseArray;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.SurfaceUtils;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -44,6 +46,7 @@ import java.io.PrintWriter;
|
||||
* They only serve to hold a collection of tasks and provide APIs like
|
||||
* {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator}
|
||||
* to perform operations in-sync with other containers.
|
||||
*
|
||||
* @see StageCoordinator
|
||||
*/
|
||||
class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
@@ -58,23 +61,31 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
/** Callback interface for listening to changes in a split-screen stage. */
|
||||
public interface StageListenerCallbacks {
|
||||
void onRootTaskAppeared();
|
||||
|
||||
void onStatusChanged(boolean visible, boolean hasChildren);
|
||||
|
||||
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
|
||||
|
||||
void onRootTaskVanished();
|
||||
void onNoLongerSupportMultiWindow();
|
||||
}
|
||||
|
||||
private final StageListenerCallbacks mCallbacks;
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
private final SurfaceSession mSurfaceSession;
|
||||
|
||||
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||
protected SurfaceControl mRootLeash;
|
||||
protected SurfaceControl mDimLayer;
|
||||
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
|
||||
private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
|
||||
|
||||
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession) {
|
||||
mCallbacks = callbacks;
|
||||
mSyncQueue = syncQueue;
|
||||
mSurfaceSession = surfaceSession;
|
||||
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
|
||||
}
|
||||
|
||||
@@ -94,6 +105,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
mRootTaskInfo = taskInfo;
|
||||
mCallbacks.onRootTaskAppeared();
|
||||
sendStatusChanged();
|
||||
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);
|
||||
@@ -146,6 +159,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
final int taskId = taskInfo.taskId;
|
||||
if (mRootTaskInfo.taskId == taskId) {
|
||||
mCallbacks.onRootTaskVanished();
|
||||
mSyncQueue.runInSync(t -> t.remove(mDimLayer));
|
||||
mRootTaskInfo = null;
|
||||
} else if (mChildrenTaskInfo.contains(taskId)) {
|
||||
mChildrenTaskInfo.remove(taskId);
|
||||
|
||||
@@ -35,6 +35,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
|
||||
@@ -48,9 +49,10 @@ import org.mockito.MockitoAnnotations;
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SplitLayoutTests extends ShellTestCase {
|
||||
@Mock SplitLayout.LayoutChangeListener mLayoutChangeListener;
|
||||
@Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler;
|
||||
@Mock SurfaceControl mRootLeash;
|
||||
@Mock DisplayImeController mDisplayImeController;
|
||||
@Mock ShellTaskOrganizer mTaskOrganizer;
|
||||
private SplitLayout mSplitLayout;
|
||||
|
||||
@Before
|
||||
@@ -60,9 +62,10 @@ public class SplitLayoutTests extends ShellTestCase {
|
||||
"TestSplitLayout",
|
||||
mContext,
|
||||
getConfiguration(false),
|
||||
mLayoutChangeListener,
|
||||
mSplitLayoutHandler,
|
||||
b -> b.setParent(mRootLeash),
|
||||
mDisplayImeController);
|
||||
mDisplayImeController,
|
||||
mTaskOrganizer);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -76,19 +79,19 @@ public class SplitLayoutTests extends ShellTestCase {
|
||||
@Test
|
||||
public void testUpdateDivideBounds() {
|
||||
mSplitLayout.updateDivideBounds(anyInt());
|
||||
verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class));
|
||||
verify(mSplitLayoutHandler).onBoundsChanging(any(SplitLayout.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetDividePosition() {
|
||||
mSplitLayout.setDividePosition(anyInt());
|
||||
verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class));
|
||||
verify(mSplitLayoutHandler).onBoundsChanged(any(SplitLayout.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDoubleTappedDivider() {
|
||||
mSplitLayout.onDoubleTappedDivider();
|
||||
verify(mLayoutChangeListener).onDoubleTappedDivider();
|
||||
verify(mSplitLayoutHandler).onDoubleTappedDivider();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,11 +101,11 @@ public class SplitLayoutTests extends ShellTestCase {
|
||||
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
|
||||
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
|
||||
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
||||
verify(mLayoutChangeListener).onSnappedToDismiss(eq(false));
|
||||
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false));
|
||||
snapTarget = getSnapTarget(0 /* position */,
|
||||
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
|
||||
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
||||
verify(mLayoutChangeListener).onSnappedToDismiss(eq(true));
|
||||
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
|
||||
}
|
||||
|
||||
private static Configuration getConfiguration(boolean isLandscape) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations;
|
||||
public class SplitWindowManagerTests extends ShellTestCase {
|
||||
@Mock SurfaceControl mSurfaceControl;
|
||||
@Mock SplitLayout mSplitLayout;
|
||||
@Mock DisplayImeController mDisplayImeController;
|
||||
private SplitWindowManager mSplitWindowManager;
|
||||
|
||||
@Before
|
||||
@@ -52,7 +50,7 @@ public class SplitWindowManagerTests extends ShellTestCase {
|
||||
final Configuration configuration = new Configuration();
|
||||
configuration.setToDefaults();
|
||||
mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
|
||||
b -> b.setParent(mSurfaceControl), mDisplayImeController);
|
||||
b -> b.setParent(mSurfaceControl));
|
||||
when(mSplitLayout.getDividerBounds()).thenReturn(
|
||||
new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
|
||||
configuration.windowConfiguration.getBounds().height()));
|
||||
|
||||
@@ -24,13 +24,13 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
|
||||
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
|
||||
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
|
||||
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
|
||||
@@ -206,7 +206,7 @@ public class DragAndDropPolicyTest {
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -218,12 +218,12 @@ public class DragAndDropPolicyTest {
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||
reset(mSplitScreenStarter);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -235,12 +235,12 @@ public class DragAndDropPolicyTest {
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||
reset(mSplitScreenStarter);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -252,7 +252,7 @@ public class DragAndDropPolicyTest {
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,7 +264,7 @@ public class DragAndDropPolicyTest {
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -277,13 +277,13 @@ public class DragAndDropPolicyTest {
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||
reset(mSplitScreenStarter);
|
||||
|
||||
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -296,13 +296,13 @@ public class DragAndDropPolicyTest {
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
||||
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||
reset(mSplitScreenStarter);
|
||||
|
||||
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
|
||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -36,7 +37,6 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
|
||||
/** Tests for {@link MainStage} */
|
||||
@SmallTest
|
||||
@@ -47,14 +47,16 @@ public class MainStageTests {
|
||||
@Mock private SyncTransactionQueue mSyncQueue;
|
||||
@Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||
@Mock private SurfaceControl mRootLeash;
|
||||
@Spy private WindowContainerTransaction mWct;
|
||||
private WindowContainerTransaction mWct = new WindowContainerTransaction();
|
||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private MainStage mMainStage;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
|
||||
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue);
|
||||
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
|
||||
mSurfaceSession);
|
||||
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -52,13 +53,15 @@ public class SideStageTests {
|
||||
@Mock private ActivityManager.RunningTaskInfo mRootTask;
|
||||
@Mock private SurfaceControl mRootLeash;
|
||||
@Spy private WindowContainerTransaction mWct;
|
||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private SideStage mSideStage;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue);
|
||||
mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
|
||||
mSurfaceSession);
|
||||
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.graphics.Rect;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.IRemoteTransition;
|
||||
import android.window.IRemoteTransitionFinishedCallback;
|
||||
import android.window.TransitionInfo;
|
||||
@@ -80,6 +81,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
@Mock private DisplayImeController mDisplayImeController;
|
||||
@Mock private TransactionPool mTransactionPool;
|
||||
@Mock private Transitions mTransitions;
|
||||
@Mock private SurfaceSession mSurfaceSession;
|
||||
private SplitLayout mSplitLayout;
|
||||
private MainStage mMainStage;
|
||||
private SideStage mSideStage;
|
||||
@@ -98,10 +100,10 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
|
||||
mSplitLayout = SplitTestUtils.createMockSplitLayout();
|
||||
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue);
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
|
||||
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue);
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
|
||||
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
|
||||
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
|
||||
|
||||
@@ -18,7 +18,7 @@ package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -73,7 +73,7 @@ public class StageCoordinatorTests extends ShellTestCase {
|
||||
public void testMoveToSideStage() {
|
||||
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
|
||||
|
||||
mStageCoordinator.moveToSideStage(task, STAGE_POSITION_BOTTOM_OR_RIGHT);
|
||||
mStageCoordinator.moveToSideStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
||||
|
||||
verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class));
|
||||
verify(mSideStage).addTask(eq(task), any(Rect.class),
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -38,6 +39,8 @@ import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@@ -52,6 +55,8 @@ public final class StageTaskListenerTests {
|
||||
@Mock private ShellTaskOrganizer mTaskOrganizer;
|
||||
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
|
||||
@Mock private SyncTransactionQueue mSyncQueue;
|
||||
@Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
|
||||
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private ActivityManager.RunningTaskInfo mRootTask;
|
||||
private StageTaskListener mStageTaskListener;
|
||||
|
||||
@@ -62,12 +67,23 @@ public final class StageTaskListenerTests {
|
||||
mTaskOrganizer,
|
||||
DEFAULT_DISPLAY,
|
||||
mCallbacks,
|
||||
mSyncQueue);
|
||||
mSyncQueue,
|
||||
mSurfaceSession);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
mRootTask.parentTaskId = INVALID_TASK_ID;
|
||||
mStageTaskListener.onTaskAppeared(mRootTask, new SurfaceControl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitsDimLayer() {
|
||||
verify(mSyncQueue).runInSync(mRunnableCaptor.capture());
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
mRunnableCaptor.getValue().runWithTransaction(t);
|
||||
t.apply();
|
||||
|
||||
assertThat(mStageTaskListener.mDimLayer).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootTaskAppeared() {
|
||||
assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId);
|
||||
|
||||
Reference in New Issue
Block a user