Merge changes from topic "staged_split_ime" into sc-dev am: 63e732e89e
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13929096 Change-Id: I3dc0b217162d4b8ff00fba5c221e17acd4457176
This commit is contained in:
@@ -16,14 +16,14 @@
|
|||||||
|
|
||||||
package com.android.wm.shell;
|
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.apppairs.AppPairsController;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
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.hidedisplaycutout.HideDisplayCutoutController;
|
||||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
|
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
|
||||||
import com.android.wm.shell.onehanded.OneHandedController;
|
import com.android.wm.shell.onehanded.OneHandedController;
|
||||||
|
import com.android.wm.shell.pip.Pip;
|
||||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
@@ -145,7 +145,7 @@ public final class ShellCommandHandlerImpl {
|
|||||||
}
|
}
|
||||||
final int taskId = new Integer(args[2]);
|
final int taskId = new Integer(args[2]);
|
||||||
final int sideStagePosition = args.length > 3
|
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));
|
mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
|
||||||
return true;
|
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_MULTI_WINDOW;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
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 static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
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.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayController;
|
import com.android.wm.shell.common.DisplayController;
|
||||||
import com.android.wm.shell.common.DisplayImeController;
|
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.SyncTransactionQueue;
|
||||||
import com.android.wm.shell.common.split.SplitLayout;
|
import com.android.wm.shell.common.split.SplitLayout;
|
||||||
|
|
||||||
@@ -45,7 +50,7 @@ import java.io.PrintWriter;
|
|||||||
* {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
|
* {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
|
||||||
* Also includes all UI for managing the pair like the divider.
|
* 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 static final String TAG = AppPair.class.getSimpleName();
|
||||||
|
|
||||||
private ActivityManager.RunningTaskInfo mRootTaskInfo;
|
private ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||||
@@ -54,6 +59,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
|||||||
private SurfaceControl mTaskLeash1;
|
private SurfaceControl mTaskLeash1;
|
||||||
private ActivityManager.RunningTaskInfo mTaskInfo2;
|
private ActivityManager.RunningTaskInfo mTaskInfo2;
|
||||||
private SurfaceControl mTaskLeash2;
|
private SurfaceControl mTaskLeash2;
|
||||||
|
private SurfaceControl mDimLayer1;
|
||||||
|
private SurfaceControl mDimLayer2;
|
||||||
|
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
|
|
||||||
private final AppPairsController mController;
|
private final AppPairsController mController;
|
||||||
private final SyncTransactionQueue mSyncQueue;
|
private final SyncTransactionQueue mSyncQueue;
|
||||||
@@ -101,7 +109,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
|||||||
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 */,
|
||||||
b -> b.setParent(mRootTaskLeash), mDisplayImeController);
|
b -> b.setParent(mRootTaskLeash), mDisplayImeController,
|
||||||
|
mController.getTaskOrganizer());
|
||||||
|
|
||||||
final WindowContainerToken token1 = task1.token;
|
final WindowContainerToken token1 = task1.token;
|
||||||
final WindowContainerToken token2 = task2.token;
|
final WindowContainerToken token2 = task2.token;
|
||||||
@@ -153,9 +162,13 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
|||||||
} else if (taskInfo.taskId == getTaskId1()) {
|
} else if (taskInfo.taskId == getTaskId1()) {
|
||||||
mTaskInfo1 = taskInfo;
|
mTaskInfo1 = taskInfo;
|
||||||
mTaskLeash1 = leash;
|
mTaskLeash1 = leash;
|
||||||
|
mSyncQueue.runInSync(t -> mDimLayer1 =
|
||||||
|
SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession));
|
||||||
} else if (taskInfo.taskId == getTaskId2()) {
|
} else if (taskInfo.taskId == getTaskId2()) {
|
||||||
mTaskInfo2 = taskInfo;
|
mTaskInfo2 = taskInfo;
|
||||||
mTaskLeash2 = leash;
|
mTaskLeash2 = leash;
|
||||||
|
mSyncQueue.runInSync(t -> mDimLayer2 =
|
||||||
|
SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession));
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
|
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
|
@Override
|
||||||
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||||
if (taskInfo.taskId == getRootTaskId()) {
|
if (taskInfo.taskId == getRootTaskId()) {
|
||||||
// We don't want to release this object back to the pool since the root task went away.
|
// We don't want to release this object back to the pool since the root task went away.
|
||||||
mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
|
mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
|
||||||
} else if (taskInfo.taskId == getTaskId1() || taskInfo.taskId == getTaskId2()) {
|
} else if (taskInfo.taskId == getTaskId1()) {
|
||||||
mController.unpair(mRootTaskInfo.taskId);
|
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
|
@Override
|
||||||
public void onBoundsChanging(SplitLayout layout) {
|
public void onBoundsChanging(SplitLayout layout) {
|
||||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
mSyncQueue.runInSync(t ->
|
||||||
if (dividerLeash == null) return;
|
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBoundsChanged(SplitLayout layout) {
|
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();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
wct.setBounds(mTaskInfo1.token, bounds1)
|
layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
|
||||||
.setBounds(mTaskInfo2.token, bounds2);
|
mSyncQueue.queue(wct);
|
||||||
mController.getTaskOrganizer().applyTransaction(wct);
|
mSyncQueue.runInSync(t ->
|
||||||
mSyncQueue.runInSync(t -> t
|
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.internal.policy.DividerSnapAlgorithm;
|
||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
import com.android.wm.shell.animation.Interpolators;
|
import com.android.wm.shell.animation.Interpolators;
|
||||||
import com.android.wm.shell.common.DisplayImeController;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Divider for multi window splits.
|
* Divider for multi window splits.
|
||||||
*/
|
*/
|
||||||
public class DividerView extends FrameLayout implements View.OnTouchListener,
|
public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||||
DisplayImeController.ImePositionProcessor {
|
|
||||||
public static final long TOUCH_ANIMATION_DURATION = 150;
|
public static final long TOUCH_ANIMATION_DURATION = 150;
|
||||||
public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
|
public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
|
||||||
|
|
||||||
@@ -98,12 +96,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
|
|||||||
setOnTouchListener(this);
|
setOnTouchListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onImeVisibilityChanged(int displayId, boolean isShowing) {
|
|
||||||
if (displayId != getDisplay().getDisplayId()) return;
|
|
||||||
setInteractive(!isShowing);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
if (mSplitLayout == null || !mInteractive) {
|
if (mSplitLayout == null || !mInteractive) {
|
||||||
@@ -217,7 +209,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
|
|||||||
mViewHost.relayout(lp);
|
mViewHost.relayout(lp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInteractive(boolean interactive) {
|
void setInteractive(boolean interactive) {
|
||||||
if (interactive == mInteractive) return;
|
if (interactive == mInteractive) return;
|
||||||
mInteractive = interactive;
|
mInteractive = interactive;
|
||||||
releaseTouching();
|
releaseTouching();
|
||||||
|
|||||||
@@ -25,16 +25,22 @@ import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_D
|
|||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.window.WindowContainerToken;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.animation.Interpolators;
|
import com.android.wm.shell.animation.Interpolators;
|
||||||
import com.android.wm.shell.common.DisplayImeController;
|
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
|
* Records and handles layout of splits. Helps to calculate proper bounds when configuration or
|
||||||
* divide position changes.
|
* 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 mDividerWindowWidth;
|
||||||
private final int mDividerInsets;
|
private final int mDividerInsets;
|
||||||
private final int mDividerSize;
|
private final int mDividerSize;
|
||||||
@@ -51,8 +82,11 @@ public class SplitLayout {
|
|||||||
private final Rect mDividerBounds = new Rect();
|
private final Rect mDividerBounds = new Rect();
|
||||||
private final Rect mBounds1 = new Rect();
|
private final Rect mBounds1 = new Rect();
|
||||||
private final Rect mBounds2 = new Rect();
|
private final Rect mBounds2 = new Rect();
|
||||||
private final LayoutChangeListener mLayoutChangeListener;
|
private final SplitLayoutHandler mSplitLayoutHandler;
|
||||||
private final SplitWindowManager mSplitWindowManager;
|
private final SplitWindowManager mSplitWindowManager;
|
||||||
|
private final DisplayImeController mDisplayImeController;
|
||||||
|
private final ImePositionProcessor mImePositionProcessor;
|
||||||
|
private final ShellTaskOrganizer mTaskOrganizer;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private DividerSnapAlgorithm mDividerSnapAlgorithm;
|
private DividerSnapAlgorithm mDividerSnapAlgorithm;
|
||||||
@@ -60,18 +94,21 @@ public class SplitLayout {
|
|||||||
private boolean mInitialized = false;
|
private boolean mInitialized = false;
|
||||||
|
|
||||||
public SplitLayout(String windowName, Context context, Configuration configuration,
|
public SplitLayout(String windowName, Context context, Configuration configuration,
|
||||||
LayoutChangeListener layoutChangeListener,
|
SplitLayoutHandler splitLayoutHandler,
|
||||||
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
|
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
|
||||||
DisplayImeController displayImeController) {
|
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
|
||||||
mContext = context.createConfigurationContext(configuration);
|
mContext = context.createConfigurationContext(configuration);
|
||||||
mLayoutChangeListener = layoutChangeListener;
|
mSplitLayoutHandler = splitLayoutHandler;
|
||||||
|
mDisplayImeController = displayImeController;
|
||||||
mSplitWindowManager = new SplitWindowManager(
|
mSplitWindowManager = new SplitWindowManager(
|
||||||
windowName, mContext, configuration, parentContainerCallbacks,
|
windowName, mContext, configuration, parentContainerCallbacks);
|
||||||
displayImeController);
|
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);
|
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);
|
com.android.internal.R.dimen.docked_stack_divider_insets);
|
||||||
mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
|
mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
|
||||||
|
|
||||||
@@ -82,17 +119,17 @@ public class SplitLayout {
|
|||||||
|
|
||||||
/** Gets bounds of the primary split. */
|
/** Gets bounds of the primary split. */
|
||||||
public Rect getBounds1() {
|
public Rect getBounds1() {
|
||||||
return mBounds1;
|
return new Rect(mBounds1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets bounds of the secondary split. */
|
/** Gets bounds of the secondary split. */
|
||||||
public Rect getBounds2() {
|
public Rect getBounds2() {
|
||||||
return mBounds2;
|
return new Rect(mBounds2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets bounds of divider window. */
|
/** Gets bounds of divider window. */
|
||||||
public Rect getDividerBounds() {
|
public Rect getDividerBounds() {
|
||||||
return mDividerBounds;
|
return new Rect(mDividerBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns leash of the current divider bar. */
|
/** Returns leash of the current divider bar. */
|
||||||
@@ -153,6 +190,7 @@ public class SplitLayout {
|
|||||||
if (mInitialized) return;
|
if (mInitialized) return;
|
||||||
mInitialized = true;
|
mInitialized = true;
|
||||||
mSplitWindowManager.init(this);
|
mSplitWindowManager.init(this);
|
||||||
|
mDisplayImeController.addPositionProcessor(mImePositionProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Releases the surface holding the current {@link DividerView}. */
|
/** Releases the surface holding the current {@link DividerView}. */
|
||||||
@@ -160,6 +198,8 @@ public class SplitLayout {
|
|||||||
if (!mInitialized) return;
|
if (!mInitialized) return;
|
||||||
mInitialized = false;
|
mInitialized = false;
|
||||||
mSplitWindowManager.release();
|
mSplitWindowManager.release();
|
||||||
|
mDisplayImeController.removePositionProcessor(mImePositionProcessor);
|
||||||
|
mImePositionProcessor.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,14 +208,14 @@ public class SplitLayout {
|
|||||||
*/
|
*/
|
||||||
void updateDivideBounds(int position) {
|
void updateDivideBounds(int position) {
|
||||||
updateBounds(position);
|
updateBounds(position);
|
||||||
mLayoutChangeListener.onBoundsChanging(this);
|
|
||||||
mSplitWindowManager.setResizingSplits(true);
|
mSplitWindowManager.setResizingSplits(true);
|
||||||
|
mSplitLayoutHandler.onBoundsChanging(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDividePosition(int position) {
|
void setDividePosition(int position) {
|
||||||
mDividePosition = position;
|
mDividePosition = position;
|
||||||
updateBounds(mDividePosition);
|
updateBounds(mDividePosition);
|
||||||
mLayoutChangeListener.onBoundsChanged(this);
|
mSplitLayoutHandler.onBoundsChanged(this);
|
||||||
mSplitWindowManager.setResizingSplits(false);
|
mSplitWindowManager.setResizingSplits(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,11 +232,11 @@ public class SplitLayout {
|
|||||||
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
|
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
|
||||||
switch (snapTarget.flag) {
|
switch (snapTarget.flag) {
|
||||||
case FLAG_DISMISS_START:
|
case FLAG_DISMISS_START:
|
||||||
mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */);
|
mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */);
|
||||||
mSplitWindowManager.setResizingSplits(false);
|
mSplitWindowManager.setResizingSplits(false);
|
||||||
break;
|
break;
|
||||||
case FLAG_DISMISS_END:
|
case FLAG_DISMISS_END:
|
||||||
mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */);
|
mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */);
|
||||||
mSplitWindowManager.setResizingSplits(false);
|
mSplitWindowManager.setResizingSplits(false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -206,7 +246,7 @@ public class SplitLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onDoubleTappedDivider() {
|
void onDoubleTappedDivider() {
|
||||||
mLayoutChangeListener.onDoubleTappedDivider();
|
mSplitLayoutHandler.onDoubleTappedDivider();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,8 +305,38 @@ public class SplitLayout {
|
|||||||
return bounds.width() > bounds.height();
|
return bounds.width() > bounds.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Listens layout change event. */
|
/** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
|
||||||
public interface LayoutChangeListener {
|
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. */
|
/** Calls when dismissing split. */
|
||||||
void onSnappedToDismiss(boolean snappedToEnd);
|
void onSnappedToDismiss(boolean snappedToEnd);
|
||||||
|
|
||||||
@@ -279,5 +349,133 @@ public class SplitLayout {
|
|||||||
/** Calls when user double tapped on the divider bar. */
|
/** Calls when user double tapped on the divider bar. */
|
||||||
default void onDoubleTappedDivider() {
|
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 androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.wm.shell.R;
|
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.
|
* 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 static final String TAG = SplitWindowManager.class.getSimpleName();
|
||||||
|
|
||||||
private final String mWindowName;
|
private final String mWindowName;
|
||||||
private final DisplayImeController mDisplayImeController;
|
|
||||||
private final ParentContainerCallbacks mParentContainerCallbacks;
|
private final ParentContainerCallbacks mParentContainerCallbacks;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private SurfaceControlViewHost mViewHost;
|
private SurfaceControlViewHost mViewHost;
|
||||||
@@ -68,13 +66,11 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SplitWindowManager(String windowName, Context context, Configuration config,
|
public SplitWindowManager(String windowName, Context context, Configuration config,
|
||||||
ParentContainerCallbacks parentContainerCallbacks,
|
ParentContainerCallbacks parentContainerCallbacks) {
|
||||||
DisplayImeController displayImeController) {
|
|
||||||
super(config, null /* rootSurface */, null /* hostInputToken */);
|
super(config, null /* rootSurface */, null /* hostInputToken */);
|
||||||
mContext = context.createConfigurationContext(config);
|
mContext = context.createConfigurationContext(config);
|
||||||
mParentContainerCallbacks = parentContainerCallbacks;
|
mParentContainerCallbacks = parentContainerCallbacks;
|
||||||
mWindowName = windowName;
|
mWindowName = windowName;
|
||||||
mDisplayImeController = displayImeController;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -128,7 +124,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
|||||||
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
|
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||||
mViewHost.setView(mDividerView, lp);
|
mViewHost.setView(mDividerView, lp);
|
||||||
mDividerView.setup(splitLayout, mViewHost);
|
mDividerView.setup(splitLayout, mViewHost);
|
||||||
mDisplayImeController.addPositionProcessor(mDividerView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,7 +132,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
|||||||
*/
|
*/
|
||||||
void release() {
|
void release() {
|
||||||
if (mDividerView != null) {
|
if (mDividerView != null) {
|
||||||
mDisplayImeController.removePositionProcessor(mDividerView);
|
|
||||||
mDividerView = null;
|
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) {
|
void setResizingSplits(boolean resizing) {
|
||||||
if (resizing == mResizingSplits) return;
|
if (resizing == mResizingSplits) return;
|
||||||
try {
|
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_TASK_ID;
|
||||||
import static android.content.Intent.EXTRA_USER;
|
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_FULLSCREEN;
|
||||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
|
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_LEFT;
|
||||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
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.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_SIDE;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.wm.shell.common.DisplayLayout;
|
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.SplitScreen.StageType;
|
||||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
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;
|
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
|
||||||
|
|
||||||
@StageType int stage = STAGE_TYPE_UNDEFINED;
|
@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) {
|
if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
|
||||||
// Update launch options for the split side we are targeting.
|
// 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) {
|
if (!inSplitScreen) {
|
||||||
// Launch in the side stage if we are not in split-screen already.
|
// Launch in the side stage if we are not in split-screen already.
|
||||||
stage = STAGE_TYPE_SIDE;
|
stage = STAGE_TYPE_SIDE;
|
||||||
@@ -219,7 +219,7 @@ public class DragAndDropPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startClipDescription(ClipDescription description, Intent intent,
|
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 isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
|
||||||
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
|
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
|
||||||
final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
|
final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
|
||||||
@@ -291,12 +291,12 @@ public class DragAndDropPolicy {
|
|||||||
* Interface for actually committing the task launches.
|
* Interface for actually committing the task launches.
|
||||||
*/
|
*/
|
||||||
public interface Starter {
|
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);
|
@Nullable Bundle options);
|
||||||
void startShortcut(String packageName, String shortcutId, @StageType int stage,
|
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,
|
void startIntent(PendingIntent intent, Intent fillInIntent,
|
||||||
@StageType int stage, @StagePosition int position,
|
@StageType int stage, @SplitPosition int position,
|
||||||
@Nullable Bundle options);
|
@Nullable Bundle options);
|
||||||
void enterSplitScreen(int taskId, boolean leftOrTop);
|
void enterSplitScreen(int taskId, boolean leftOrTop);
|
||||||
void exitSplitScreen();
|
void exitSplitScreen();
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
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.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
|
|
||||||
@@ -146,21 +147,11 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG);
|
ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG);
|
||||||
|
|
||||||
// Initialize dim surfaces:
|
// 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();
|
SurfaceControl.Transaction t = getTransaction();
|
||||||
t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
|
mPrimaryDim = SurfaceUtils.makeDimLayer(
|
||||||
t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
|
t, mPrimarySurface, "Primary Divider Dim", mSurfaceSession);
|
||||||
t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
|
mSecondaryDim = SurfaceUtils.makeDimLayer(
|
||||||
t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
|
t, mSecondarySurface, "Secondary Divider Dim", mSurfaceSession);
|
||||||
t.apply();
|
t.apply();
|
||||||
releaseTransaction(t);
|
releaseTransaction(t);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
@@ -36,8 +37,9 @@ class MainStage extends StageTaskListener {
|
|||||||
private boolean mIsActive = false;
|
private boolean mIsActive = false;
|
||||||
|
|
||||||
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
|
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||||
super(taskOrganizer, displayId, callbacks, syncQueue);
|
SurfaceSession surfaceSession) {
|
||||||
|
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isActive() {
|
boolean isActive() {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
|
|||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
@@ -33,8 +34,9 @@ class SideStage extends StageTaskListener {
|
|||||||
private static final String TAG = SideStage.class.getSimpleName();
|
private static final String TAG = SideStage.class.getSimpleName();
|
||||||
|
|
||||||
SideStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
SideStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
|
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||||
super(taskOrganizer, displayId, callbacks, syncQueue);
|
SurfaceSession surfaceSession) {
|
||||||
|
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
|
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.wm.shell.splitscreen;
|
|||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
|
|
||||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||||
|
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to engage split-screen feature.
|
* Interface to engage split-screen feature.
|
||||||
@@ -26,29 +27,6 @@ import com.android.wm.shell.common.annotations.ExternalThread;
|
|||||||
*/
|
*/
|
||||||
@ExternalThread
|
@ExternalThread
|
||||||
public interface SplitScreen {
|
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.
|
* 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.
|
* 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. */
|
/** Callback interface for listening to changes in a split-screen stage. */
|
||||||
interface SplitScreenListener {
|
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);
|
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 android.view.Display.DEFAULT_DISPLAY;
|
||||||
|
|
||||||
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
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.common.split.SplitLayout.SPLIT_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_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_UNDEFINED;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
|
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_SIDE;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
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.SyncTransactionQueue;
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
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.draganddrop.DragAndDropPolicy;
|
||||||
import com.android.wm.shell.splitscreen.ISplitScreenListener;
|
import com.android.wm.shell.splitscreen.ISplitScreenListener;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
@@ -122,7 +123,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
return mStageCoordinator.isSplitScreenVisible();
|
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);
|
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
|
||||||
if (task == null) {
|
if (task == null) {
|
||||||
throw new IllegalArgumentException("Unknown taskId" + taskId);
|
throw new IllegalArgumentException("Unknown taskId" + taskId);
|
||||||
@@ -131,7 +132,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||||
@SplitScreen.StagePosition int sideStagePosition) {
|
@SplitPosition int sideStagePosition) {
|
||||||
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
|
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +140,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
return mStageCoordinator.removeFromSideStage(taskId);
|
return mStageCoordinator.removeFromSideStage(taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
|
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
|
||||||
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
|
|
||||||
public void enterSplitScreen(int taskId, boolean leftOrTop) {
|
public void enterSplitScreen(int taskId, boolean leftOrTop) {
|
||||||
moveToSideStage(taskId,
|
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() {
|
public void exitSplitScreen() {
|
||||||
@@ -173,7 +174,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startTask(int taskId, @SplitScreen.StageType int stage,
|
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);
|
options = resolveStartStage(stage, position, options);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -184,7 +185,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startShortcut(String packageName, String shortcutId,
|
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) {
|
@Nullable Bundle options, UserHandle user) {
|
||||||
options = resolveStartStage(stage, position, options);
|
options = resolveStartStage(stage, position, options);
|
||||||
|
|
||||||
@@ -199,7 +200,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startIntent(PendingIntent intent, Intent fillInIntent,
|
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) {
|
@Nullable Bundle options) {
|
||||||
options = resolveStartStage(stage, position, options);
|
options = resolveStartStage(stage, position, options);
|
||||||
|
|
||||||
@@ -211,11 +212,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Bundle resolveStartStage(@SplitScreen.StageType int stage,
|
private Bundle resolveStartStage(@SplitScreen.StageType int stage,
|
||||||
@SplitScreen.StagePosition int position, @Nullable Bundle options) {
|
@SplitPosition int position, @Nullable Bundle options) {
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case STAGE_TYPE_UNDEFINED: {
|
case STAGE_TYPE_UNDEFINED: {
|
||||||
// Use the stage of the specified position is valid.
|
// Use the stage of the specified position is valid.
|
||||||
if (position != STAGE_POSITION_UNDEFINED) {
|
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||||
if (position == mStageCoordinator.getSideStagePosition()) {
|
if (position == mStageCoordinator.getSideStagePosition()) {
|
||||||
options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
|
options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
|
||||||
} else {
|
} else {
|
||||||
@@ -228,7 +229,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STAGE_TYPE_SIDE: {
|
case STAGE_TYPE_SIDE: {
|
||||||
if (position != STAGE_POSITION_UNDEFINED) {
|
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||||
mStageCoordinator.setSideStagePosition(position);
|
mStageCoordinator.setSideStagePosition(position);
|
||||||
} else {
|
} else {
|
||||||
position = mStageCoordinator.getSideStagePosition();
|
position = mStageCoordinator.getSideStagePosition();
|
||||||
@@ -240,10 +241,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STAGE_TYPE_MAIN: {
|
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.
|
// Set the side stage opposite of what we want to the main stage.
|
||||||
final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT
|
final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
|
||||||
? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
|
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||||
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
||||||
} else {
|
} else {
|
||||||
position = mStageCoordinator.getMainStagePosition();
|
position = mStageCoordinator.getMainStagePosition();
|
||||||
@@ -418,7 +419,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
@Override
|
@Override
|
||||||
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
|
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
|
||||||
int sideTaskId, @Nullable Bundle sideOptions,
|
int sideTaskId, @Nullable Bundle sideOptions,
|
||||||
@SplitScreen.StagePosition int sidePosition,
|
@SplitPosition int sidePosition,
|
||||||
@Nullable IRemoteTransition remoteTransition) {
|
@Nullable IRemoteTransition remoteTransition) {
|
||||||
executeRemoteCallWithTaskPermission(mController, "startTasks",
|
executeRemoteCallWithTaskPermission(mController, "startTasks",
|
||||||
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
|
(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.TRANSIT_TO_FRONT;
|
||||||
import static android.view.WindowManager.transitTypeToString;
|
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.common.split.SplitLayout.SPLIT_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_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_MAIN;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
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.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.window.DisplayAreaInfo;
|
import android.window.DisplayAreaInfo;
|
||||||
import android.window.IRemoteTransition;
|
import android.window.IRemoteTransition;
|
||||||
import android.window.TransitionInfo;
|
import android.window.TransitionInfo;
|
||||||
import android.window.TransitionRequestInfo;
|
import android.window.TransitionRequestInfo;
|
||||||
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
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.SyncTransactionQueue;
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
import com.android.wm.shell.common.split.SplitLayout;
|
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.protolog.ShellProtoLogGroup;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
|
|
||||||
@@ -84,7 +87,7 @@ import java.util.List;
|
|||||||
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
|
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
|
||||||
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
|
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
|
||||||
*/
|
*/
|
||||||
class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||||
RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
|
RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
|
||||||
|
|
||||||
private static final String TAG = StageCoordinator.class.getSimpleName();
|
private static final String TAG = StageCoordinator.class.getSimpleName();
|
||||||
@@ -92,11 +95,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
/** internal value for mDismissTop that represents no dismiss */
|
/** internal value for mDismissTop that represents no dismiss */
|
||||||
private static final int NO_DISMISS = -2;
|
private static final int NO_DISMISS = -2;
|
||||||
|
|
||||||
|
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
|
|
||||||
private final MainStage mMainStage;
|
private final MainStage mMainStage;
|
||||||
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
|
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
|
||||||
private final SideStage mSideStage;
|
private final SideStage mSideStage;
|
||||||
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
|
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 final int mDisplayId;
|
||||||
private SplitLayout mSplitLayout;
|
private SplitLayout mSplitLayout;
|
||||||
@@ -115,8 +121,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
/** 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 int mDismissTop = NO_DISMISS;
|
@SplitScreen.StageType int mDismissTop = NO_DISMISS;
|
||||||
|
|
||||||
private final Runnable mOnTransitionAnimationComplete = () -> {
|
private final Runnable mOnTransitionAnimationComplete = () -> {
|
||||||
// If still playing, let it finish.
|
// If still playing, let it finish.
|
||||||
if (!isSplitScreenVisible()) {
|
if (!isSplitScreenVisible()) {
|
||||||
@@ -137,8 +143,18 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
mSyncQueue = syncQueue;
|
mSyncQueue = syncQueue;
|
||||||
mRootTDAOrganizer = rootTDAOrganizer;
|
mRootTDAOrganizer = rootTDAOrganizer;
|
||||||
mTaskOrganizer = taskOrganizer;
|
mTaskOrganizer = taskOrganizer;
|
||||||
mMainStage = new MainStage(mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue);
|
mMainStage = new MainStage(
|
||||||
mSideStage = new SideStage(mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue);
|
mTaskOrganizer,
|
||||||
|
mDisplayId,
|
||||||
|
mMainStageListener,
|
||||||
|
mSyncQueue,
|
||||||
|
mSurfaceSession);
|
||||||
|
mSideStage = new SideStage(
|
||||||
|
mTaskOrganizer,
|
||||||
|
mDisplayId,
|
||||||
|
mSideStageListener,
|
||||||
|
mSyncQueue,
|
||||||
|
mSurfaceSession);
|
||||||
mDisplayImeController = displayImeController;
|
mDisplayImeController = displayImeController;
|
||||||
mRootTDAOrganizer.registerListener(displayId, this);
|
mRootTDAOrganizer.registerListener(displayId, this);
|
||||||
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
|
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
|
||||||
@@ -176,7 +192,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||||
@SplitScreen.StagePosition int sideStagePosition) {
|
@SplitPosition int sideStagePosition) {
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
setSideStagePosition(sideStagePosition);
|
setSideStagePosition(sideStagePosition);
|
||||||
mMainStage.activate(getMainStageBounds(), wct);
|
mMainStage.activate(getMainStageBounds(), wct);
|
||||||
@@ -201,7 +217,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
|
|
||||||
/** 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, @SplitScreen.StagePosition int sidePosition,
|
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
|
||||||
@Nullable IRemoteTransition remoteTransition) {
|
@Nullable IRemoteTransition remoteTransition) {
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
mainOptions = mainOptions != null ? mainOptions : new Bundle();
|
mainOptions = mainOptions != null ? mainOptions : new Bundle();
|
||||||
@@ -225,20 +241,22 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
|
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SplitScreen.StagePosition int getSideStagePosition() {
|
@SplitLayout.SplitPosition
|
||||||
|
int getSideStagePosition() {
|
||||||
return mSideStagePosition;
|
return mSideStagePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SplitScreen.StagePosition int getMainStagePosition() {
|
@SplitLayout.SplitPosition
|
||||||
return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
int getMainStagePosition() {
|
||||||
? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
|
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 */);
|
setSideStagePosition(sideStagePosition, true /* updateVisibility */);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition,
|
private void setSideStagePosition(@SplitPosition int sideStagePosition,
|
||||||
boolean updateVisibility) {
|
boolean updateVisibility) {
|
||||||
if (mSideStagePosition == sideStagePosition) return;
|
if (mSideStagePosition == sideStagePosition) return;
|
||||||
mSideStagePosition = sideStagePosition;
|
mSideStagePosition = sideStagePosition;
|
||||||
@@ -289,7 +307,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
|
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);
|
addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
|
||||||
|
|
||||||
if (!mMainStage.isActive()) {
|
if (!mMainStage.isActive()) {
|
||||||
@@ -487,8 +505,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
@Override
|
@Override
|
||||||
public void onSnappedToDismiss(boolean bottomOrRight) {
|
public void onSnappedToDismiss(boolean bottomOrRight) {
|
||||||
final boolean mainStageToTop =
|
final boolean mainStageToTop =
|
||||||
bottomOrRight ? mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT
|
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
|
||||||
: mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT;
|
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
|
||||||
if (ENABLE_SHELL_TRANSITIONS) {
|
if (ENABLE_SHELL_TRANSITIONS) {
|
||||||
onSnappedToDismissTransition(mainStageToTop);
|
onSnappedToDismissTransition(mainStageToTop);
|
||||||
return;
|
return;
|
||||||
@@ -497,55 +515,49 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBoundsChanging(SplitLayout layout) {
|
public void onDoubleTappedDivider() {
|
||||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||||
if (dividerLeash == null) return;
|
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT);
|
||||||
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()));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDoubleTappedDivider() {
|
public void onBoundsChanging(SplitLayout layout) {
|
||||||
setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
final StageTaskListener topLeftStage =
|
||||||
? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT);
|
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
|
@Override
|
||||||
public void onBoundsChanged(SplitLayout layout) {
|
public void onBoundsChanged(SplitLayout layout) {
|
||||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
final StageTaskListener topLeftStage =
|
||||||
if (dividerLeash == null) return;
|
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
||||||
final Rect mainStageBounds = getMainStageBounds();
|
final StageTaskListener bottomRightStage =
|
||||||
final Rect sideStageBounds = getSideStageBounds();
|
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
|
||||||
mMainStage.setBounds(mainStageBounds, wct);
|
|
||||||
mSideStage.setBounds(sideStageBounds, wct);
|
|
||||||
mTaskOrganizer.applyTransaction(wct);
|
|
||||||
|
|
||||||
mSyncQueue.runInSync(t -> t
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
// Resets layer of divider bar to make sure it is always on top.
|
layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
|
||||||
.setLayer(dividerLeash, Integer.MAX_VALUE)
|
mSyncQueue.queue(wct);
|
||||||
.setPosition(dividerLeash,
|
mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
|
||||||
mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)
|
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
|
||||||
.setPosition(mMainStage.mRootLeash,
|
}
|
||||||
mainStageBounds.left, mainStageBounds.top)
|
|
||||||
.setPosition(mSideStage.mRootLeash,
|
@Override
|
||||||
sideStageBounds.left, sideStageBounds.top)
|
public int getSplitItemPosition(WindowContainerToken token) {
|
||||||
// Resets crop to apply new surface bounds directly.
|
if (token == null) {
|
||||||
.setWindowCrop(mMainStage.mRootLeash, null)
|
return SPLIT_POSITION_UNDEFINED;
|
||||||
.setWindowCrop(mSideStage.mRootLeash, null));
|
}
|
||||||
|
|
||||||
|
if (token.equals(mMainStage.mRootTaskInfo.getToken())) {
|
||||||
|
return getMainStagePosition();
|
||||||
|
} else if (token.equals(mSideStage.mRootTaskInfo.getToken())) {
|
||||||
|
return getSideStagePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
return SPLIT_POSITION_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -555,7 +567,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
|
||||||
mDisplayAreaInfo.configuration, this,
|
mDisplayAreaInfo.configuration, this,
|
||||||
b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
|
b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
|
||||||
mDisplayImeController);
|
mDisplayImeController, mTaskOrganizer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,12 +586,12 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Rect getSideStageBounds() {
|
private Rect getSideStageBounds() {
|
||||||
return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||||
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
|
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Rect getMainStageBounds() {
|
private Rect getMainStageBounds() {
|
||||||
return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
|
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||||
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
|
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,7 +754,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
|||||||
|
|
||||||
// Update local states (before animating).
|
// Update local states (before animating).
|
||||||
setDividerVisibility(true);
|
setDividerVisibility(true);
|
||||||
setSideStagePosition(STAGE_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
|
setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
|
||||||
setSplitsVisible(true);
|
setSplitsVisible(true);
|
||||||
|
|
||||||
addDividerBarToTransition(info, t, true /* show */);
|
addDividerBarToTransition(info, t, true /* show */);
|
||||||
|
|||||||
@@ -29,11 +29,13 @@ import android.graphics.Point;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
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.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
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
|
* They only serve to hold a collection of tasks and provide APIs like
|
||||||
* {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator}
|
* {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator}
|
||||||
* to perform operations in-sync with other containers.
|
* to perform operations in-sync with other containers.
|
||||||
|
*
|
||||||
* @see StageCoordinator
|
* @see StageCoordinator
|
||||||
*/
|
*/
|
||||||
class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
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. */
|
/** Callback interface for listening to changes in a split-screen stage. */
|
||||||
public interface StageListenerCallbacks {
|
public interface StageListenerCallbacks {
|
||||||
void onRootTaskAppeared();
|
void onRootTaskAppeared();
|
||||||
|
|
||||||
void onStatusChanged(boolean visible, boolean hasChildren);
|
void onStatusChanged(boolean visible, boolean hasChildren);
|
||||||
|
|
||||||
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
|
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
|
||||||
|
|
||||||
void onRootTaskVanished();
|
void onRootTaskVanished();
|
||||||
void onNoLongerSupportMultiWindow();
|
void onNoLongerSupportMultiWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final StageListenerCallbacks mCallbacks;
|
private final StageListenerCallbacks mCallbacks;
|
||||||
private final SyncTransactionQueue mSyncQueue;
|
private final SyncTransactionQueue mSyncQueue;
|
||||||
|
private final SurfaceSession mSurfaceSession;
|
||||||
|
|
||||||
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
|
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
|
||||||
protected SurfaceControl mRootLeash;
|
protected SurfaceControl mRootLeash;
|
||||||
|
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<>();
|
||||||
|
|
||||||
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
|
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
|
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||||
|
SurfaceSession surfaceSession) {
|
||||||
mCallbacks = callbacks;
|
mCallbacks = callbacks;
|
||||||
mSyncQueue = syncQueue;
|
mSyncQueue = syncQueue;
|
||||||
|
mSurfaceSession = surfaceSession;
|
||||||
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
|
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +105,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
mRootTaskInfo = taskInfo;
|
mRootTaskInfo = taskInfo;
|
||||||
mCallbacks.onRootTaskAppeared();
|
mCallbacks.onRootTaskAppeared();
|
||||||
sendStatusChanged();
|
sendStatusChanged();
|
||||||
|
mSyncQueue.runInSync(t -> 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);
|
||||||
@@ -146,6 +159,7 @@ 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;
|
||||||
} else if (mChildrenTaskInfo.contains(taskId)) {
|
} else if (mChildrenTaskInfo.contains(taskId)) {
|
||||||
mChildrenTaskInfo.remove(taskId);
|
mChildrenTaskInfo.remove(taskId);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.ShellTestCase;
|
import com.android.wm.shell.ShellTestCase;
|
||||||
import com.android.wm.shell.common.DisplayImeController;
|
import com.android.wm.shell.common.DisplayImeController;
|
||||||
|
|
||||||
@@ -48,9 +49,10 @@ import org.mockito.MockitoAnnotations;
|
|||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class SplitLayoutTests extends ShellTestCase {
|
public class SplitLayoutTests extends ShellTestCase {
|
||||||
@Mock SplitLayout.LayoutChangeListener mLayoutChangeListener;
|
@Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler;
|
||||||
@Mock SurfaceControl mRootLeash;
|
@Mock SurfaceControl mRootLeash;
|
||||||
@Mock DisplayImeController mDisplayImeController;
|
@Mock DisplayImeController mDisplayImeController;
|
||||||
|
@Mock ShellTaskOrganizer mTaskOrganizer;
|
||||||
private SplitLayout mSplitLayout;
|
private SplitLayout mSplitLayout;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -60,9 +62,10 @@ public class SplitLayoutTests extends ShellTestCase {
|
|||||||
"TestSplitLayout",
|
"TestSplitLayout",
|
||||||
mContext,
|
mContext,
|
||||||
getConfiguration(false),
|
getConfiguration(false),
|
||||||
mLayoutChangeListener,
|
mSplitLayoutHandler,
|
||||||
b -> b.setParent(mRootLeash),
|
b -> b.setParent(mRootLeash),
|
||||||
mDisplayImeController);
|
mDisplayImeController,
|
||||||
|
mTaskOrganizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -76,19 +79,19 @@ public class SplitLayoutTests extends ShellTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testUpdateDivideBounds() {
|
public void testUpdateDivideBounds() {
|
||||||
mSplitLayout.updateDivideBounds(anyInt());
|
mSplitLayout.updateDivideBounds(anyInt());
|
||||||
verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class));
|
verify(mSplitLayoutHandler).onBoundsChanging(any(SplitLayout.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetDividePosition() {
|
public void testSetDividePosition() {
|
||||||
mSplitLayout.setDividePosition(anyInt());
|
mSplitLayout.setDividePosition(anyInt());
|
||||||
verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class));
|
verify(mSplitLayoutHandler).onBoundsChanged(any(SplitLayout.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnDoubleTappedDivider() {
|
public void testOnDoubleTappedDivider() {
|
||||||
mSplitLayout.onDoubleTappedDivider();
|
mSplitLayout.onDoubleTappedDivider();
|
||||||
verify(mLayoutChangeListener).onDoubleTappedDivider();
|
verify(mSplitLayoutHandler).onDoubleTappedDivider();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -98,11 +101,11 @@ public class SplitLayoutTests extends ShellTestCase {
|
|||||||
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
|
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
|
||||||
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
|
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
|
||||||
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
||||||
verify(mLayoutChangeListener).onSnappedToDismiss(eq(false));
|
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false));
|
||||||
snapTarget = getSnapTarget(0 /* position */,
|
snapTarget = getSnapTarget(0 /* position */,
|
||||||
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
|
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
|
||||||
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
||||||
verify(mLayoutChangeListener).onSnappedToDismiss(eq(true));
|
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Configuration getConfiguration(boolean isLandscape) {
|
private static Configuration getConfiguration(boolean isLandscape) {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.wm.shell.ShellTestCase;
|
import com.android.wm.shell.ShellTestCase;
|
||||||
import com.android.wm.shell.common.DisplayImeController;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations;
|
|||||||
public class SplitWindowManagerTests extends ShellTestCase {
|
public class SplitWindowManagerTests extends ShellTestCase {
|
||||||
@Mock SurfaceControl mSurfaceControl;
|
@Mock SurfaceControl mSurfaceControl;
|
||||||
@Mock SplitLayout mSplitLayout;
|
@Mock SplitLayout mSplitLayout;
|
||||||
@Mock DisplayImeController mDisplayImeController;
|
|
||||||
private SplitWindowManager mSplitWindowManager;
|
private SplitWindowManager mSplitWindowManager;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -52,7 +50,7 @@ public class SplitWindowManagerTests extends ShellTestCase {
|
|||||||
final Configuration configuration = new Configuration();
|
final Configuration configuration = new Configuration();
|
||||||
configuration.setToDefaults();
|
configuration.setToDefaults();
|
||||||
mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
|
mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
|
||||||
b -> b.setParent(mSurfaceControl), mDisplayImeController);
|
b -> b.setParent(mSurfaceControl));
|
||||||
when(mSplitLayout.getDividerBounds()).thenReturn(
|
when(mSplitLayout.getDividerBounds()).thenReturn(
|
||||||
new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
|
new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
|
||||||
configuration.windowConfiguration.getBounds().height()));
|
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_SHORTCUT;
|
||||||
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
|
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_FULLSCREEN;
|
||||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
|
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_LEFT;
|
||||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
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.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_SIDE;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
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);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -218,12 +218,12 @@ public class DragAndDropPolicyTest {
|
|||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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);
|
reset(mSplitScreenStarter);
|
||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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
|
@Test
|
||||||
@@ -235,12 +235,12 @@ public class DragAndDropPolicyTest {
|
|||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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);
|
reset(mSplitScreenStarter);
|
||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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
|
@Test
|
||||||
@@ -252,7 +252,7 @@ public class DragAndDropPolicyTest {
|
|||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -264,7 +264,7 @@ public class DragAndDropPolicyTest {
|
|||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
verify(mSplitScreenStarter).startIntent(any(), any(),
|
||||||
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
|
eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -277,13 +277,13 @@ public class DragAndDropPolicyTest {
|
|||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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);
|
reset(mSplitScreenStarter);
|
||||||
|
|
||||||
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
|
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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
|
@Test
|
||||||
@@ -296,13 +296,13 @@ public class DragAndDropPolicyTest {
|
|||||||
|
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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);
|
reset(mSplitScreenStarter);
|
||||||
|
|
||||||
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
|
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
|
||||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
|
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
|
||||||
verify(mSplitScreenStarter).startIntent(any(), any(),
|
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
|
@Test
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -36,7 +37,6 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.Spy;
|
|
||||||
|
|
||||||
/** Tests for {@link MainStage} */
|
/** Tests for {@link MainStage} */
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -47,14 +47,16 @@ public class MainStageTests {
|
|||||||
@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;
|
||||||
@Spy private WindowContainerTransaction mWct;
|
private WindowContainerTransaction mWct = new WindowContainerTransaction();
|
||||||
|
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
private MainStage mMainStage;
|
private MainStage mMainStage;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
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(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
|
||||||
|
mSurfaceSession);
|
||||||
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
|
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -52,13 +53,15 @@ public class SideStageTests {
|
|||||||
@Mock private ActivityManager.RunningTaskInfo mRootTask;
|
@Mock private ActivityManager.RunningTaskInfo mRootTask;
|
||||||
@Mock private SurfaceControl mRootLeash;
|
@Mock private SurfaceControl mRootLeash;
|
||||||
@Spy private WindowContainerTransaction mWct;
|
@Spy private WindowContainerTransaction mWct;
|
||||||
|
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
private SideStage mSideStage;
|
private SideStage mSideStage;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
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);
|
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import android.graphics.Rect;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
import android.window.IRemoteTransition;
|
import android.window.IRemoteTransition;
|
||||||
import android.window.IRemoteTransitionFinishedCallback;
|
import android.window.IRemoteTransitionFinishedCallback;
|
||||||
import android.window.TransitionInfo;
|
import android.window.TransitionInfo;
|
||||||
@@ -80,6 +81,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
|||||||
@Mock private DisplayImeController mDisplayImeController;
|
@Mock private DisplayImeController mDisplayImeController;
|
||||||
@Mock private TransactionPool mTransactionPool;
|
@Mock private TransactionPool mTransactionPool;
|
||||||
@Mock private Transitions mTransitions;
|
@Mock private Transitions mTransitions;
|
||||||
|
@Mock private SurfaceSession mSurfaceSession;
|
||||||
private SplitLayout mSplitLayout;
|
private SplitLayout mSplitLayout;
|
||||||
private MainStage mMainStage;
|
private MainStage mMainStage;
|
||||||
private SideStage mSideStage;
|
private SideStage mSideStage;
|
||||||
@@ -98,10 +100,10 @@ public class SplitTransitionTests extends ShellTestCase {
|
|||||||
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(mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue);
|
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
|
||||||
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||||
mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue);
|
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
|
||||||
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,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ package com.android.wm.shell.splitscreen;
|
|||||||
|
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
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.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -73,7 +73,7 @@ public class StageCoordinatorTests extends ShellTestCase {
|
|||||||
public void testMoveToSideStage() {
|
public void testMoveToSideStage() {
|
||||||
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
|
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(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class));
|
||||||
verify(mSideStage).addTask(eq(task), any(Rect.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.app.ActivityManager;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceSession;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
@@ -38,6 +39,8 @@ import com.android.wm.shell.common.SyncTransactionQueue;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
@@ -52,6 +55,8 @@ public final class StageTaskListenerTests {
|
|||||||
@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;
|
||||||
|
@Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
|
||||||
|
private SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
private ActivityManager.RunningTaskInfo mRootTask;
|
private ActivityManager.RunningTaskInfo mRootTask;
|
||||||
private StageTaskListener mStageTaskListener;
|
private StageTaskListener mStageTaskListener;
|
||||||
|
|
||||||
@@ -62,12 +67,23 @@ public final class StageTaskListenerTests {
|
|||||||
mTaskOrganizer,
|
mTaskOrganizer,
|
||||||
DEFAULT_DISPLAY,
|
DEFAULT_DISPLAY,
|
||||||
mCallbacks,
|
mCallbacks,
|
||||||
mSyncQueue);
|
mSyncQueue,
|
||||||
|
mSurfaceSession);
|
||||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||||
mRootTask.parentTaskId = INVALID_TASK_ID;
|
mRootTask.parentTaskId = INVALID_TASK_ID;
|
||||||
mStageTaskListener.onTaskAppeared(mRootTask, new SurfaceControl());
|
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
|
@Test
|
||||||
public void testRootTaskAppeared() {
|
public void testRootTaskAppeared() {
|
||||||
assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId);
|
assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId);
|
||||||
|
|||||||
Reference in New Issue
Block a user