Merge "drag-n-drop support for main/side stage split-screen"
This commit is contained in:
committed by
Android (Google) Code Review
commit
91464139ff
@@ -100,7 +100,7 @@ public class ShellInitImpl {
|
||||
mSplitScreenOptional.ifPresent(SplitScreen::onOrganizerRegistered);
|
||||
|
||||
// Bind the splitscreen impl to the drag drop controller
|
||||
mDragAndDropController.initialize(mLegacySplitScreenOptional);
|
||||
mDragAndDropController.initialize(mSplitScreenOptional);
|
||||
|
||||
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
|
||||
mTransitions.register(mShellTaskOrganizer);
|
||||
|
||||
@@ -135,7 +135,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
|
||||
final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
|
||||
final DividerSnapAlgorithm.SnapTarget snapTarget =
|
||||
mSplitLayout.findSnapTarget(position, velocity);
|
||||
mSplitLayout.findSnapTarget(position, velocity, false /* hardDismiss */);
|
||||
mSplitLayout.snapToTarget(position, snapTarget);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -188,11 +188,11 @@ public class SplitLayout {
|
||||
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
|
||||
switch (snapTarget.flag) {
|
||||
case FLAG_DISMISS_START:
|
||||
mLayoutChangeListener.onSnappedToDismiss(false /* snappedToEnd */);
|
||||
mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
break;
|
||||
case FLAG_DISMISS_END:
|
||||
mLayoutChangeListener.onSnappedToDismiss(true /* snappedToEnd */);
|
||||
mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
break;
|
||||
default:
|
||||
@@ -207,9 +207,11 @@ public class SplitLayout {
|
||||
|
||||
/**
|
||||
* Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity.
|
||||
* If hardDismiss is set to {@code true}, it will be harder to reach dismiss target.
|
||||
*/
|
||||
public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity) {
|
||||
return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity);
|
||||
public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity,
|
||||
boolean hardDismiss) {
|
||||
return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
|
||||
}
|
||||
|
||||
private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) {
|
||||
|
||||
@@ -52,7 +52,7 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -66,7 +66,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
|
||||
|
||||
private final Context mContext;
|
||||
private final DisplayController mDisplayController;
|
||||
private LegacySplitScreen mLegacySplitScreen;
|
||||
private SplitScreen mSplitScreen;
|
||||
|
||||
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
|
||||
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
@@ -76,8 +76,8 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
|
||||
mDisplayController = displayController;
|
||||
}
|
||||
|
||||
public void initialize(Optional<LegacySplitScreen> splitscreen) {
|
||||
mLegacySplitScreen = splitscreen.orElse(null);
|
||||
public void initialize(Optional<SplitScreen> splitscreen) {
|
||||
mSplitScreen = splitscreen.orElse(null);
|
||||
mDisplayController.addDisplayWindowListener(this);
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
|
||||
R.layout.global_drop_target, null);
|
||||
rootView.setOnDragListener(this);
|
||||
rootView.setVisibility(View.INVISIBLE);
|
||||
DragLayout dragLayout = new DragLayout(context, mLegacySplitScreen);
|
||||
DragLayout dragLayout = new DragLayout(context, mSplitScreen);
|
||||
rootView.addView(dragLayout,
|
||||
new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
try {
|
||||
|
||||
@@ -34,6 +34,8 @@ import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPL
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
@@ -59,13 +61,12 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The policy for handling drag and drop operations to shell.
|
||||
@@ -77,22 +78,22 @@ public class DragAndDropPolicy {
|
||||
private final Context mContext;
|
||||
private final ActivityTaskManager mActivityTaskManager;
|
||||
private final Starter mStarter;
|
||||
private final LegacySplitScreen mLegacySplitScreen;
|
||||
private final SplitScreen mSplitScreen;
|
||||
private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
|
||||
|
||||
private DragSession mSession;
|
||||
|
||||
public DragAndDropPolicy(Context context, LegacySplitScreen legacySplitScreen) {
|
||||
this(context, ActivityTaskManager.getInstance(), legacySplitScreen,
|
||||
new DefaultStarter(context, legacySplitScreen));
|
||||
public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
|
||||
this(context, ActivityTaskManager.getInstance(), splitScreen,
|
||||
new DefaultStarter(context, splitScreen));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager,
|
||||
LegacySplitScreen legacySplitScreen, Starter starter) {
|
||||
SplitScreen splitScreen, Starter starter) {
|
||||
mContext = context;
|
||||
mActivityTaskManager = activityTaskManager;
|
||||
mLegacySplitScreen = legacySplitScreen;
|
||||
mSplitScreen = splitScreen;
|
||||
mStarter = starter;
|
||||
}
|
||||
|
||||
@@ -122,64 +123,54 @@ public class DragAndDropPolicy {
|
||||
final int ih = h - insets.top - insets.bottom;
|
||||
final int l = insets.left;
|
||||
final int t = insets.top;
|
||||
final boolean isVerticalSplit = mSession.isPhone && !mSession.displayLayout.isLandscape();
|
||||
if (mSession.dragItemSupportsSplitscreen
|
||||
&& mSession.runningTaskActType == ACTIVITY_TYPE_STANDARD
|
||||
&& mSession.runningTaskWinMode == WINDOWING_MODE_FULLSCREEN
|
||||
&& mSession.runningTaskIsResizeable) {
|
||||
// Allow splitting when there is a fullscreen standard activity running
|
||||
if (isVerticalSplit) {
|
||||
// TODO(b/169894807): For now, only allow splitting to the right/bottom until we
|
||||
// have split pairs
|
||||
mTargets.add(new Target(TYPE_FULLSCREEN,
|
||||
new Rect(l, t, l + iw, t + ih / 2),
|
||||
new Rect(l, t, l + iw, t + ih),
|
||||
new Rect(0, 0, w, h)));
|
||||
mTargets.add(new Target(TYPE_SPLIT_BOTTOM,
|
||||
new Rect(l, t + ih / 2, l + iw, t + ih),
|
||||
new Rect(l, t + ih / 2, l + iw, t + ih),
|
||||
new Rect(0, h / 2, w, h)));
|
||||
} else {
|
||||
mTargets.add(new Target(TYPE_FULLSCREEN,
|
||||
new Rect(l, t, l + iw / 2, t + ih),
|
||||
new Rect(l, t, l + iw, t + ih),
|
||||
new Rect(0, 0, w, h)));
|
||||
mTargets.add(new Target(TYPE_SPLIT_RIGHT,
|
||||
new Rect(l + iw / 2, t, l + iw, t + ih),
|
||||
new Rect(l + iw / 2, t, l + iw, t + ih),
|
||||
new Rect(w / 2, 0, w, h)));
|
||||
}
|
||||
} else if (mSession.dragItemSupportsSplitscreen
|
||||
&& mLegacySplitScreen != null
|
||||
&& mLegacySplitScreen.isDividerVisible()) {
|
||||
final Rect displayRegion = new Rect(l, t, l + iw, t + ih);
|
||||
final Rect fullscreenDrawRegion = new Rect(displayRegion);
|
||||
final Rect fullscreenHitRegion = new Rect(displayRegion);
|
||||
final boolean inLandscape = mSession.displayLayout.isLandscape();
|
||||
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
|
||||
// We allow splitting if we are already in split-screen or the running task is a standard
|
||||
// task in fullscreen mode.
|
||||
final boolean allowSplit = inSplitScreen
|
||||
|| (mSession.runningTaskActType == ACTIVITY_TYPE_STANDARD
|
||||
&& mSession.runningTaskWinMode == WINDOWING_MODE_FULLSCREEN);
|
||||
if (allowSplit) {
|
||||
// Already split, allow replacing existing split task
|
||||
// TODO(b/169894807): For now, only allow replacing the non-primary task until we have
|
||||
// split pairs
|
||||
final Rect secondarySplitRawBounds =
|
||||
mLegacySplitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds();
|
||||
final Rect secondarySplitBounds = new Rect(secondarySplitRawBounds);
|
||||
secondarySplitBounds.intersect(new Rect(l, t, l + iw, t + ih));
|
||||
if (isVerticalSplit) {
|
||||
mTargets.add(new Target(TYPE_FULLSCREEN,
|
||||
new Rect(l, t, l + iw, secondarySplitRawBounds.top),
|
||||
new Rect(l, t, l + iw, t + ih),
|
||||
new Rect(0, 0, w, secondarySplitRawBounds.top)));
|
||||
final Rect topOrLeftBounds = new Rect();
|
||||
final Rect bottomOrRightBounds = new Rect();
|
||||
mSplitScreen.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
|
||||
topOrLeftBounds.intersect(displayRegion);
|
||||
bottomOrRightBounds.intersect(displayRegion);
|
||||
|
||||
if (inLandscape) {
|
||||
final Rect leftHitRegion = new Rect();
|
||||
final Rect leftDrawRegion = topOrLeftBounds;
|
||||
final Rect rightHitRegion = new Rect();
|
||||
final Rect rightDrawRegion = bottomOrRightBounds;
|
||||
|
||||
displayRegion.splitVertically(leftHitRegion, fullscreenHitRegion, rightHitRegion);
|
||||
|
||||
mTargets.add(
|
||||
new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
|
||||
mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
|
||||
mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
|
||||
|
||||
} else {
|
||||
mTargets.add(new Target(TYPE_FULLSCREEN,
|
||||
new Rect(l, t, secondarySplitRawBounds.left, t + ih),
|
||||
new Rect(l, t, l + iw, t + ih),
|
||||
new Rect(0, 0, w, h)));
|
||||
final Rect topHitRegion = new Rect();
|
||||
final Rect topDrawRegion = topOrLeftBounds;
|
||||
final Rect bottomHitRegion = new Rect();
|
||||
final Rect bottomDrawRegion = bottomOrRightBounds;
|
||||
|
||||
displayRegion.splitHorizontally(
|
||||
topHitRegion, fullscreenHitRegion, bottomHitRegion);
|
||||
|
||||
mTargets.add(
|
||||
new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
|
||||
mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
|
||||
mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
|
||||
}
|
||||
mTargets.add(new Target(isVerticalSplit ? TYPE_SPLIT_BOTTOM : TYPE_SPLIT_RIGHT,
|
||||
new Rect(secondarySplitBounds),
|
||||
new Rect(secondarySplitBounds),
|
||||
new Rect(secondarySplitBounds)));
|
||||
} else {
|
||||
// Otherwise only show the fullscreen target
|
||||
mTargets.add(new Target(TYPE_FULLSCREEN,
|
||||
new Rect(l, t, l + iw, t + ih),
|
||||
new Rect(l, t, l + iw, t + ih),
|
||||
new Rect(0, 0, w, h)));
|
||||
// Split-screen not allowed, so only show the fullscreen target
|
||||
mTargets.add(new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
|
||||
}
|
||||
return mTargets;
|
||||
}
|
||||
@@ -208,58 +199,34 @@ public class DragAndDropPolicy {
|
||||
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
|
||||
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
|
||||
final Intent dragData = mSession.dragData;
|
||||
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
|
||||
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
|
||||
final Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
|
||||
? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
|
||||
: new Bundle();
|
||||
|
||||
boolean deferAppLaunchUntilSplit = false;
|
||||
if (target.type == TYPE_FULLSCREEN) {
|
||||
if (mLegacySplitScreen != null && mLegacySplitScreen.isDividerVisible()) {
|
||||
// If in split, remove split and launch fullscreen
|
||||
mStarter.exitSplitScreen(mSession.runningTaskId);
|
||||
} else {
|
||||
// Not in split, fall through to launch
|
||||
}
|
||||
} else {
|
||||
if (mLegacySplitScreen != null && mLegacySplitScreen.isDividerVisible()) {
|
||||
// Split is already visible, just replace the task
|
||||
// TODO(b/169894807): Since we only allow replacing the non-primary target above
|
||||
// just fall through and start the activity
|
||||
} else {
|
||||
// Not in split, enter split now
|
||||
mStarter.enterSplitScreen(mSession.runningTaskId,
|
||||
target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP);
|
||||
deferAppLaunchUntilSplit = true;
|
||||
// Exit split stages if needed
|
||||
mStarter.exitSplitScreen();
|
||||
} else if (mSplitScreen != null) {
|
||||
// Update launch options for the split side we are targeting.
|
||||
final int position = leftOrTop
|
||||
? SIDE_STAGE_POSITION_TOP_OR_LEFT : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
if (!inSplitScreen) {
|
||||
// Update the side stage position to match where we want to launch.
|
||||
mSplitScreen.setSideStagePosition(position);
|
||||
}
|
||||
mSplitScreen.updateActivityOptions(opts, position);
|
||||
}
|
||||
|
||||
final Runnable startAppRunnable = () -> {
|
||||
Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
|
||||
? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
|
||||
: null;
|
||||
if (isTask) {
|
||||
mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
|
||||
} else if (isShortcut) {
|
||||
mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
|
||||
dragData.getStringExtra(EXTRA_SHORTCUT_ID),
|
||||
opts, dragData.getParcelableExtra(EXTRA_USER));
|
||||
} else {
|
||||
mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
|
||||
}
|
||||
};
|
||||
if (deferAppLaunchUntilSplit) {
|
||||
// TODO(b/169894807): The enterSplitScreen() call above will trigger the current task
|
||||
// into split, and we should wait for home and other tasks to be moved to
|
||||
// split-secondary before trying to launch the new secondary task. This can be removed
|
||||
// once we have app-pairs.
|
||||
mLegacySplitScreen.registerInSplitScreenListener(new Consumer<Boolean>() {
|
||||
@Override
|
||||
public void accept(Boolean inSplit) {
|
||||
if (inSplit) {
|
||||
startAppRunnable.run();
|
||||
mLegacySplitScreen.unregisterInSplitScreenListener(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (isTask) {
|
||||
mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
|
||||
} else if (isShortcut) {
|
||||
mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
|
||||
dragData.getStringExtra(EXTRA_SHORTCUT_ID),
|
||||
opts, dragData.getParcelableExtra(EXTRA_USER));
|
||||
} else {
|
||||
startAppRunnable.run();
|
||||
mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +290,7 @@ public class DragAndDropPolicy {
|
||||
UserHandle user);
|
||||
void startIntent(PendingIntent intent, Bundle activityOptions);
|
||||
void enterSplitScreen(int taskId, boolean leftOrTop);
|
||||
void exitSplitScreen(int taskId);
|
||||
void exitSplitScreen();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,17 +299,17 @@ public class DragAndDropPolicy {
|
||||
*/
|
||||
private static class DefaultStarter implements Starter {
|
||||
private final Context mContext;
|
||||
private final LegacySplitScreen mLegacySplitScreen;
|
||||
private final SplitScreen mSplitScreen;
|
||||
|
||||
public DefaultStarter(Context context, LegacySplitScreen legacySplitScreen) {
|
||||
public DefaultStarter(Context context, SplitScreen splitScreen) {
|
||||
mContext = context;
|
||||
mLegacySplitScreen = legacySplitScreen;
|
||||
mSplitScreen = splitScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTask(int taskId, Bundle activityOptions) {
|
||||
try {
|
||||
ActivityTaskManager.getService().startActivityFromRecents(taskId, null);
|
||||
ActivityTaskManager.getService().startActivityFromRecents(taskId, activityOptions);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to launch task", e);
|
||||
}
|
||||
@@ -372,12 +339,14 @@ public class DragAndDropPolicy {
|
||||
|
||||
@Override
|
||||
public void enterSplitScreen(int taskId, boolean leftOrTop) {
|
||||
mLegacySplitScreen.splitPrimaryTask();
|
||||
mSplitScreen.moveToSideStage(taskId,
|
||||
leftOrTop ? SIDE_STAGE_POSITION_TOP_OR_LEFT
|
||||
: SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitSplitScreen(int taskId) {
|
||||
mLegacySplitScreen.dismissSplitToPrimaryTask();
|
||||
public void exitSplitScreen() {
|
||||
mSplitScreen.exitSplitScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,19 +375,16 @@ public class DragAndDropPolicy {
|
||||
final Rect hitRegion;
|
||||
// The approximate visual region for where the task will start
|
||||
final Rect drawRegion;
|
||||
// The
|
||||
final Rect dropTargetBounds;
|
||||
|
||||
public Target(@Type int t, Rect hit, Rect draw, Rect drop) {
|
||||
public Target(@Type int t, Rect hit, Rect draw) {
|
||||
type = t;
|
||||
hitRegion = hit;
|
||||
drawRegion = draw;
|
||||
dropTargetBounds = drop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Target {hit=" + hitRegion + " drop=" + dropTargetBounds + "}";
|
||||
return "Target {hit=" + hitRegion + " draw=" + drawRegion + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class DragLayout extends View {
|
||||
private boolean mIsShowing;
|
||||
private boolean mHasDropped;
|
||||
|
||||
public DragLayout(Context context, LegacySplitScreen splitscreen) {
|
||||
public DragLayout(Context context, SplitScreen splitscreen) {
|
||||
super(context);
|
||||
mPolicy = new DragAndDropPolicy(context, splitscreen);
|
||||
mDisplayMargin = context.getResources().getDimensionPixelSize(
|
||||
|
||||
@@ -38,12 +38,6 @@ class MainStage extends StageTaskListener {
|
||||
|
||||
private boolean mIsActive = false;
|
||||
|
||||
private static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
|
||||
private static final int[] CONTROLLED_WINDOWING_MODES =
|
||||
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
|
||||
private static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
|
||||
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
|
||||
|
||||
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) {
|
||||
super(taskOrganizer, displayId, callbacks, syncQueue);
|
||||
@@ -92,7 +86,7 @@ class MainStage extends StageTaskListener {
|
||||
null /* newParent */,
|
||||
CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
|
||||
CONTROLLED_ACTIVITY_TYPES,
|
||||
true /* onTop */)
|
||||
false /* onTop */)
|
||||
.reorder(rootToken, false /* onTop */);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.graphics.Rect;
|
||||
import android.window.WindowContainerToken;
|
||||
@@ -48,6 +50,17 @@ class SideStage extends StageTaskListener {
|
||||
.reorder(rootToken, true);
|
||||
}
|
||||
|
||||
boolean removeAllTasks(WindowContainerTransaction wct) {
|
||||
if (mChildrenTaskInfo.size() == 0) return false;
|
||||
wct.reparentTasks(
|
||||
mRootTaskInfo.token,
|
||||
null /* newParent */,
|
||||
CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
|
||||
CONTROLLED_ACTIVITY_TYPES,
|
||||
false /* onTop */);
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
|
||||
final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
|
||||
if (task == null) return false;
|
||||
@@ -57,4 +70,12 @@ class SideStage extends StageTaskListener {
|
||||
.reparent(task.token, newParent, false /* onTop */);
|
||||
return true;
|
||||
}
|
||||
|
||||
int getTopVisibleTaskId() {
|
||||
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
|
||||
final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.valueAt(i);
|
||||
if (task.isVisible) return task.taskId;
|
||||
}
|
||||
return INVALID_TASK_ID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.wm.shell.splitscreen;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.app.ActivityManager;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -61,6 +63,12 @@ public interface SplitScreen {
|
||||
void setSideStagePosition(@SideStagePosition int sideStagePosition);
|
||||
/** Hides the side-stage if it is currently visible. */
|
||||
void setSideStageVisibility(boolean visible);
|
||||
/** Removes the split-screen stages. */
|
||||
void exitSplitScreen();
|
||||
/** Gets the stage bounds. */
|
||||
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
|
||||
/** Updates the launch activity options for the split position we want to launch it in. */
|
||||
void updateActivityOptions(Bundle opts, @SideStagePosition int position);
|
||||
/** Dumps current status of split-screen. */
|
||||
void dump(@NonNull PrintWriter pw, String prefix);
|
||||
/** Called when the shell organizer has been registered. */
|
||||
|
||||
@@ -20,6 +20,8 @@ import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -69,7 +71,10 @@ public class SplitScreenController implements SplitScreen {
|
||||
@Override
|
||||
public boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition) {
|
||||
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
|
||||
return task != null && moveToSideStage(task, sideStagePosition);
|
||||
if (task == null) {
|
||||
throw new IllegalArgumentException("Unknown taskId" + taskId);
|
||||
}
|
||||
return moveToSideStage(task, sideStagePosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,6 +98,21 @@ public class SplitScreenController implements SplitScreen {
|
||||
mStageCoordinator.setSideStageVisibility(visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitSplitScreen() {
|
||||
mStageCoordinator.exitSplitScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
|
||||
mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActivityOptions(Bundle opts, @SideStagePosition int position) {
|
||||
mStageCoordinator.updateActivityOptions(opts, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + TAG);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
|
||||
@@ -25,6 +27,7 @@ import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_T
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.DisplayAreaInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
@@ -142,6 +145,31 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
}
|
||||
|
||||
void exitSplitScreen() {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
mSideStage.removeAllTasks(wct);
|
||||
mMainStage.deactivate(wct);
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
}
|
||||
|
||||
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
|
||||
outTopOrLeftBounds.set(mSplitLayout.getBounds1());
|
||||
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
|
||||
}
|
||||
|
||||
void updateActivityOptions(Bundle opts, @SplitScreen.SideStagePosition int position) {
|
||||
final StageTaskListener stage = position == mSideStagePosition ? mSideStage : mMainStage;
|
||||
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
|
||||
|
||||
if (!mMainStage.isActive()) {
|
||||
// Activate the main stage in anticipation of an app launch.
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
mMainStage.activate(getMainStageBounds(), wct);
|
||||
mSideStage.setBounds(getSideStageBounds(), wct);
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
}
|
||||
}
|
||||
|
||||
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
|
||||
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
@@ -175,6 +203,12 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
}
|
||||
}
|
||||
|
||||
if (!mainStageVisible && !sideStageVisible) {
|
||||
// Exit split-screen if both stage are not visible.
|
||||
// TODO: This is only a temporary request from UX and is likely to be removed soon...
|
||||
exitSplitScreen();
|
||||
}
|
||||
|
||||
if (mainStageVisible) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
if (sideStageVisible) {
|
||||
@@ -252,10 +286,27 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnappedToDismiss(boolean snappedToEnd) {
|
||||
// TODO: What to do...what to do...
|
||||
public void onSnappedToDismiss(boolean bottomOrRight) {
|
||||
if (mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT && bottomOrRight) {
|
||||
// Main stage was fully expanded...Just side side-stage.
|
||||
setSideStageVisibility(false);
|
||||
} else {
|
||||
// Side stage was fully expanded...Move its top task to the main stage
|
||||
// and hide side-stage.
|
||||
// TODO: Would UX prefer the side-stage go into fullscreen mode here?
|
||||
final int taskId = mSideStage.getTopVisibleTaskId();
|
||||
if (taskId == INVALID_TASK_ID) {
|
||||
throw new IllegalStateException("Side stage doesn't have visible task? "
|
||||
+ mSideStage);
|
||||
}
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
mSideStage.removeTask(taskId, mMainStage.mRootTaskInfo.getToken(), wct);
|
||||
mSideStage.setVisibility(false, wct);
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
}
|
||||
|
||||
// Reset divider position.
|
||||
mSplitLayout.resetDividerPosition();
|
||||
onBoundsChanged(mSplitLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.app.ActivityManager;
|
||||
@@ -45,6 +48,12 @@ import java.io.PrintWriter;
|
||||
class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
private static final String TAG = StageTaskListener.class.getSimpleName();
|
||||
|
||||
protected static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
|
||||
protected static final int[] CONTROLLED_WINDOWING_MODES =
|
||||
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
|
||||
protected static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
|
||||
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
|
||||
|
||||
/** Callback interface for listening to changes in a split-screen stage. */
|
||||
public interface StageListenerCallbacks {
|
||||
void onRootTaskAppeared();
|
||||
@@ -69,7 +78,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
if (!taskInfo.hasParentTask()) {
|
||||
if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
|
||||
mRootLeash = leash;
|
||||
mRootTaskInfo = taskInfo;
|
||||
mCallbacks.onRootTaskAppeared();
|
||||
@@ -78,7 +87,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
|
||||
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown task: " + taskInfo);
|
||||
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
||||
+ "\n mRootTaskInfo: " + mRootTaskInfo);
|
||||
}
|
||||
sendStatusChanged();
|
||||
}
|
||||
@@ -93,7 +103,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
updateChildTaskSurface(
|
||||
taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown task: " + taskInfo);
|
||||
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
||||
+ "\n mRootTaskInfo: " + mRootTaskInfo);
|
||||
}
|
||||
sendStatusChanged();
|
||||
}
|
||||
@@ -110,7 +121,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
mChildrenLeashes.remove(taskId);
|
||||
sendStatusChanged();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown task: " + taskInfo);
|
||||
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
||||
+ "\n mRootTaskInfo: " + mRootTaskInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +155,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
@Override
|
||||
@CallSuper
|
||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||
|
||||
final String innerPrefix = prefix + " ";
|
||||
final String childPrefix = innerPrefix + " ";
|
||||
pw.println(prefix + this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
|
||||
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.Assert.fail;
|
||||
@@ -35,7 +37,6 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
@@ -52,7 +53,6 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.RemoteException;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
@@ -61,20 +61,17 @@ import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
|
||||
import com.android.wm.shell.legacysplitscreen.DividerView;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Tests for the drag and drop policy.
|
||||
@@ -90,12 +87,13 @@ public class DragAndDropPolicyTest {
|
||||
private ActivityTaskManager mActivityTaskManager;
|
||||
|
||||
@Mock
|
||||
private LegacySplitScreen mLegacySplitScreen;
|
||||
private SplitScreen mSplitScreen;
|
||||
|
||||
@Mock
|
||||
private DragAndDropPolicy.Starter mStarter;
|
||||
|
||||
private DisplayLayout mDisplayLayout;
|
||||
private DisplayLayout mLandscapeDisplayLayout;
|
||||
private DisplayLayout mPortraitDisplayLayout;
|
||||
private Insets mInsets;
|
||||
private DragAndDropPolicy mPolicy;
|
||||
|
||||
@@ -116,25 +114,19 @@ public class DragAndDropPolicyTest {
|
||||
Resources res = mock(Resources.class);
|
||||
Configuration config = new Configuration();
|
||||
doReturn(config).when(res).getConfiguration();
|
||||
doReturn(res).when(mContext).getResources();
|
||||
DisplayInfo info = new DisplayInfo();
|
||||
info.logicalWidth = 100;
|
||||
info.logicalWidth = 200;
|
||||
info.logicalHeight = 100;
|
||||
mDisplayLayout = new DisplayLayout(info, res, false, false);
|
||||
mLandscapeDisplayLayout = new DisplayLayout(info, res, false, false);
|
||||
DisplayInfo info2 = new DisplayInfo();
|
||||
info.logicalWidth = 100;
|
||||
info.logicalHeight = 200;
|
||||
mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
|
||||
mInsets = Insets.of(0, 0, 0, 0);
|
||||
|
||||
DividerView divider = mock(DividerView.class);
|
||||
doReturn(divider).when(mLegacySplitScreen).getDividerView();
|
||||
doReturn(new Rect(50, 0, 100, 100)).when(divider)
|
||||
.getNonMinimizedSplitScreenSecondaryBounds();
|
||||
|
||||
doAnswer((Answer<Void>) invocation -> {
|
||||
Consumer<Boolean> callback = invocation.getArgument(0);
|
||||
callback.accept(true);
|
||||
return null;
|
||||
}).when(mLegacySplitScreen).registerInSplitScreenListener(any());
|
||||
|
||||
mPolicy = new DragAndDropPolicy(
|
||||
mContext, mActivityTaskManager, mLegacySplitScreen, mStarter);
|
||||
mContext, mActivityTaskManager, mSplitScreen, mStarter);
|
||||
mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
|
||||
mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
|
||||
setClipDataResizeable(mNonResizeableActivityClipData, false);
|
||||
@@ -149,7 +141,6 @@ public class DragAndDropPolicyTest {
|
||||
mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
|
||||
ACTIVITY_TYPE_STANDARD);
|
||||
|
||||
setIsPhone(false);
|
||||
setInSplitScreen(false);
|
||||
setRunningTask(mFullscreenAppTask);
|
||||
}
|
||||
@@ -199,22 +190,14 @@ public class DragAndDropPolicyTest {
|
||||
: ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
|
||||
}
|
||||
|
||||
private void setIsPhone(boolean isPhone) {
|
||||
Resources res = mock(Resources.class);
|
||||
Configuration config = mock(Configuration.class);
|
||||
config.smallestScreenWidthDp = isPhone ? 400 : 800;
|
||||
doReturn(config).when(res).getConfiguration();
|
||||
doReturn(res).when(mContext).getResources();
|
||||
}
|
||||
|
||||
private void setInSplitScreen(boolean inSplitscreen) {
|
||||
doReturn(inSplitscreen).when(mLegacySplitScreen).isDividerVisible();
|
||||
doReturn(inSplitscreen).when(mSplitScreen).isSplitScreenVisible();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() throws RemoteException {
|
||||
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
|
||||
setRunningTask(mHomeTask);
|
||||
mPolicy.start(mDisplayLayout, mActivityClipData);
|
||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
|
||||
ArrayList<Target> targets = assertExactTargetTypes(
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
|
||||
|
||||
@@ -223,77 +206,66 @@ public class DragAndDropPolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragAppOverFullscreenApp_expectSplitScreenAndFullscreenTargets()
|
||||
throws RemoteException {
|
||||
public void testDragAppOverFullscreenApp_expectSplitScreenAndFullscreenTargets() {
|
||||
setRunningTask(mFullscreenAppTask);
|
||||
mPolicy.start(mDisplayLayout, mActivityClipData);
|
||||
// TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
|
||||
// pairs
|
||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
|
||||
ArrayList<Target> targets = assertExactTargetTypes(
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_RIGHT);
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mStarter).exitSplitScreen();
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
reset(mStarter);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
|
||||
verify(mStarter).enterSplitScreen(anyInt(), eq(false));
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenAndFullscreenTargets()
|
||||
throws RemoteException {
|
||||
setIsPhone(true);
|
||||
public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenAndFullscreenTargets() {
|
||||
setRunningTask(mFullscreenAppTask);
|
||||
mPolicy.start(mDisplayLayout, mActivityClipData);
|
||||
// TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
|
||||
// pairs
|
||||
mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
|
||||
ArrayList<Target> targets = assertExactTargetTypes(
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_BOTTOM);
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mStarter).exitSplitScreen();
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
reset(mStarter);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
|
||||
verify(mStarter).enterSplitScreen(anyInt(), eq(false));
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragAppOverFullscreenNonResizeableApp_expectOnlyFullscreenTargets()
|
||||
throws RemoteException {
|
||||
public void testDragAppOverFullscreenNonResizeableApp_expectOnlyFullscreenTargets() {
|
||||
setRunningTask(mNonResizeableFullscreenAppTask);
|
||||
mPolicy.start(mDisplayLayout, mActivityClipData);
|
||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
|
||||
ArrayList<Target> targets = assertExactTargetTypes(
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragNonResizeableAppOverFullscreenApp_expectOnlyFullscreenTargets()
|
||||
throws RemoteException {
|
||||
public void testDragNonResizeableAppOverFullscreenApp_expectOnlyFullscreenTargets() {
|
||||
setRunningTask(mFullscreenAppTask);
|
||||
mPolicy.start(mDisplayLayout, mNonResizeableActivityClipData);
|
||||
mPolicy.start(mLandscapeDisplayLayout, mNonResizeableActivityClipData);
|
||||
ArrayList<Target> targets = assertExactTargetTypes(
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragAppOverSplitApp_expectFullscreenAndSplitTargets() throws RemoteException {
|
||||
public void testDragAppOverSplitApp_expectFullscreenAndSplitTargets() {
|
||||
setInSplitScreen(true);
|
||||
setRunningTask(mSplitPrimaryAppTask);
|
||||
mPolicy.start(mDisplayLayout, mActivityClipData);
|
||||
// TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
|
||||
// pairs
|
||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
|
||||
ArrayList<Target> targets = assertExactTargetTypes(
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_RIGHT);
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
@@ -305,16 +277,12 @@ public class DragAndDropPolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragAppOverSplitAppPhone_expectFullscreenAndVerticalSplitTargets()
|
||||
throws RemoteException {
|
||||
setIsPhone(true);
|
||||
public void testDragAppOverSplitAppPhone_expectFullscreenAndVerticalSplitTargets() {
|
||||
setInSplitScreen(true);
|
||||
setRunningTask(mSplitPrimaryAppTask);
|
||||
mPolicy.start(mDisplayLayout, mActivityClipData);
|
||||
// TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
|
||||
// pairs
|
||||
mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
|
||||
ArrayList<Target> targets = assertExactTargetTypes(
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_BOTTOM);
|
||||
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
|
||||
|
||||
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
|
||||
verify(mStarter).startIntent(any(), any());
|
||||
@@ -326,9 +294,9 @@ public class DragAndDropPolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetHitRects() throws RemoteException {
|
||||
public void testTargetHitRects() {
|
||||
setRunningTask(mFullscreenAppTask);
|
||||
mPolicy.start(mDisplayLayout, mActivityClipData);
|
||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
|
||||
ArrayList<Target> targets = mPolicy.getTargets(mInsets);
|
||||
for (Target t : targets) {
|
||||
assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.left, t.hitRegion.top) == t);
|
||||
|
||||
Reference in New Issue
Block a user