Loading libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +6 −4 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +5 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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); } Loading @@ -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 { Loading libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +87 −121 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading @@ -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; } Loading Loading @@ -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))); } mTargets.add(new Target(isVerticalSplit ? TYPE_SPLIT_BOTTOM : TYPE_SPLIT_RIGHT, new Rect(secondarySplitBounds), new Rect(secondarySplitBounds), new Rect(secondarySplitBounds))); 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)); } } 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; } Loading Loading @@ -208,32 +199,26 @@ 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) { Loading @@ -243,24 +228,6 @@ public class DragAndDropPolicy { } 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); } } }); } else { startAppRunnable.run(); } } /** Loading Loading @@ -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(); } /** Loading @@ -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); } Loading Loading @@ -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(); } } Loading Loading @@ -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 + "}"; } } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +6 −4 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +5 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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); } Loading @@ -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 { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +87 −121 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading @@ -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; } Loading Loading @@ -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))); } mTargets.add(new Target(isVerticalSplit ? TYPE_SPLIT_BOTTOM : TYPE_SPLIT_RIGHT, new Rect(secondarySplitBounds), new Rect(secondarySplitBounds), new Rect(secondarySplitBounds))); 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)); } } 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; } Loading Loading @@ -208,32 +199,26 @@ 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) { Loading @@ -243,24 +228,6 @@ public class DragAndDropPolicy { } 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); } } }); } else { startAppRunnable.run(); } } /** Loading Loading @@ -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(); } /** Loading @@ -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); } Loading Loading @@ -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(); } } Loading Loading @@ -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 + "}"; } } }