summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Chang <chengjeff@google.com> 2022-12-16 13:32:57 +0800
committer Jeff Chang <chengjeff@google.com> 2023-02-10 11:36:00 +0800
commitdb4c59f87b68d98cc406d16c00155c279307aa19 (patch)
treeedf0cfa189f6e081e8aefbf0c4457d337ddf1f43
parentc694f8b49455a3140fbb5a8c4afda47f335ac7b4 (diff)
Make PIP into split screen
Improve the interaction between split and PIP. e.g support drag and drop pip app into split, start the pip into split, start Intent and task into split. Bug: 259161859 Test: PIP is activated and drag pip app into split. Video : https://drive.google.com/file/d/10VdfV7jfGHGRLrEdxAIWFKPhBVgwFTrI/view?usp=share_link&resourcekey=0-jZD7-EzUGc95WQIND3Jxrw Change-Id: I2d72eeb2a799e94bb53fe42ce2b439644ec8520e
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java94
5 files changed, 122 insertions, 11 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index 9e0a48b13413..e2106e478bb3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -216,7 +216,6 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler.
args.argi1 = homeTaskVisible ? 1 : 0;
args.argi2 = clearedTask ? 1 : 0;
args.argi3 = wasVisible ? 1 : 0;
- mMainHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
mMainHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index aad27b991db9..6412e80d671a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1404,7 +1404,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
- final boolean isPipTopLeft = isPipTopLeft();
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1450,9 +1449,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
null /* callback */, false /* withStartDelay */);
});
} else {
- applyFinishBoundsResize(wct, direction, isPipTopLeft);
+ applyFinishBoundsResize(wct, direction, false);
}
} else {
+ final boolean isPipTopLeft =
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && isPipToTopLeft();
applyFinishBoundsResize(wct, direction, isPipTopLeft);
}
@@ -1521,6 +1522,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return topLeft.contains(mPipBoundsState.getBounds());
}
+ private boolean isPipToTopLeft() {
+ if (!mSplitScreenOptional.isPresent()) {
+ return false;
+ }
+ return mSplitScreenOptional.get().getActivateSplitPosition(mTaskInfo)
+ == SPLIT_POSITION_TOP_OR_LEFT;
+ }
+
/**
* The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
* and can be overridden to restore to an alternate windowing mode.
@@ -1652,8 +1661,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final Rect topLeft = new Rect();
final Rect bottomRight = new Rect();
mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
- final boolean isPipTopLeft = isPipTopLeft();
- destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
+ destinationBoundsOut.set(isPipToTopLeft() ? topLeft : bottomRight);
return true;
}
@@ -1737,6 +1745,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceControlTransactionFactory = factory;
}
+ public boolean isLaunchToSplit(TaskInfo taskInfo) {
+ return mSplitScreenOptional.isPresent()
+ && mSplitScreenOptional.get().isLaunchToSplit(taskInfo);
+ }
+
/**
* Dumps internal states.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index d86468a838d9..541dc19c3829 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -570,8 +570,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
if (task.getWindowingMode() != WINDOWING_MODE_PINNED) {
return;
}
- mTouchHandler.getMotionHelper().expandLeavePip(
- clearedTask /* skipAnimation */);
+ if (mPipTaskOrganizer.isLaunchToSplit(task)) {
+ mTouchHandler.getMotionHelper().expandIntoSplit();
+ } else {
+ mTouchHandler.getMotionHelper().expandLeavePip(
+ clearedTask /* skipAnimation */);
+ }
}
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 36da4cf12706..c7ad4fdcacf0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -42,6 +42,7 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -421,6 +422,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.goToFullscreenFromSplit();
}
+ public boolean isLaunchToSplit(TaskInfo taskInfo) {
+ return mStageCoordinator.isLaunchToSplit(taskInfo);
+ }
+
+ public int getActivateSplitPosition(TaskInfo taskInfo) {
+ return mStageCoordinator.getActivateSplitPosition(taskInfo);
+ }
+
public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
final int[] result = new int[1];
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 0f18ddaba218..a42820d2e765 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -74,6 +74,7 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IActivityTaskManager;
import android.app.PendingIntent;
+import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -124,6 +125,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
@@ -208,6 +210,36 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private DefaultMixedHandler mMixedHandler;
private final Toast mSplitUnsupportedToast;
+ private SplitRequest mSplitRequest;
+
+ class SplitRequest {
+ @SplitPosition
+ int mActivatePosition;
+ int mActivateTaskId;
+ int mActivateTaskId2;
+ Intent mStartIntent;
+ Intent mStartIntent2;
+
+ SplitRequest(int taskId, Intent startIntent, int position) {
+ mActivateTaskId = taskId;
+ mStartIntent = startIntent;
+ mActivatePosition = position;
+ }
+ SplitRequest(Intent startIntent, int position) {
+ mStartIntent = startIntent;
+ mActivatePosition = position;
+ }
+ SplitRequest(Intent startIntent, Intent startIntent2, int position) {
+ mStartIntent = startIntent;
+ mStartIntent2 = startIntent2;
+ mActivatePosition = position;
+ }
+ SplitRequest(int taskId1, int taskId2, int position) {
+ mActivateTaskId = taskId1;
+ mActivateTaskId2 = taskId2;
+ mActivatePosition = position;
+ }
+ }
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
@@ -393,6 +425,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setSideStagePosition(sideStagePosition, wct);
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
targetStage.evictAllChildren(evictWct);
+
+ // Apply surface bounds before animation start.
+ SurfaceControl.Transaction startT = mTransactionPool.acquire();
+ if (startT != null) {
+ updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
+ startT.apply();
+ mTransactionPool.release(startT);
+ }
+ // reparent the task to an invisible split root will make the activity invisible. Reorder
+ // the root task to front to make the entering transition from pip to split smooth.
+ wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, true);
+ wct.reorder(targetStage.mRootTaskInfo.token, true);
+ wct.setForceTranslucent(targetStage.mRootTaskInfo.token, true);
+ // prevent the fling divider to center transition
+ mIsDropEntering = true;
+
targetStage.addTask(task, wct);
if (ENABLE_SHELL_TRANSITIONS) {
@@ -547,7 +596,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- if (isEnteringSplit && !openingToSide) {
+ if (isEnteringSplit && !openingToSide && apps != null) {
mMainExecutor.execute(() -> exitSplitScreen(
mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
EXIT_REASON_UNKNOWN));
@@ -587,7 +636,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (isEnteringSplit && mLogger.isEnterRequestedByDrag()) {
updateWindowBounds(mSplitLayout, wct);
}
-
+ mSplitRequest = new SplitRequest(intent.getIntent(), position);
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
@@ -686,6 +735,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.startTask(taskId1, options1);
+ mSplitRequest = new SplitRequest(taskId1, taskId2, splitPosition);
startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter,
instanceId);
}
@@ -719,6 +769,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
} else {
wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+ mSplitRequest = new SplitRequest(pendingIntent1.getIntent(),
+ pendingIntent2 != null ? pendingIntent2.getIntent() : null, splitPosition);
}
startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
splitPosition, splitRatio, adapter, instanceId);
@@ -743,6 +795,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+ mSplitRequest = new SplitRequest(taskId, pendingIntent.getIntent(), splitPosition);
startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
instanceId);
}
@@ -815,7 +868,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
mIsSplitEntering = true;
-
+ if (mSplitRequest == null) {
+ mSplitRequest = new SplitRequest(mainTaskId,
+ mainPendingIntent != null ? mainPendingIntent.getIntent() : null,
+ sidePosition);
+ }
setSideStagePosition(sidePosition, wct);
if (!mMainStage.isActive()) {
mMainStage.activate(wct, false /* reparent */);
@@ -906,6 +963,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
WindowContainerTransaction evictWct) {
mIsSplitEntering = false;
mShouldUpdateRecents = true;
+ mSplitRequest = null;
// If any stage has no child after animation finished, it means that split will display
// nothing, such status will happen if task and intent is same app but not support
// multi-instance, we should exit split and expand that app as full screen.
@@ -1739,7 +1797,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.init();
final WindowContainerTransaction wct = new WindowContainerTransaction();
- if (mLogger.isEnterRequestedByDrag()) {
+ if (mIsDropEntering) {
prepareEnterSplitScreen(wct);
} else {
// TODO (b/238697912) : Add the validation to prevent entering non-recovered status
@@ -1764,6 +1822,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true;
+ mSplitRequest = null;
updateRecentTasksSplitPair();
if (!mLogger.hasStartedSession()) {
@@ -2316,6 +2375,33 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
}
+ boolean isLaunchToSplit(TaskInfo taskInfo) {
+ return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED;
+ }
+
+ int getActivateSplitPosition(TaskInfo taskInfo) {
+ if (mSplitRequest == null || taskInfo == null) {
+ return SPLIT_POSITION_UNDEFINED;
+ }
+ if (mSplitRequest.mActivateTaskId != 0
+ && mSplitRequest.mActivateTaskId2 == taskInfo.taskId) {
+ return mSplitRequest.mActivatePosition;
+ }
+ if (mSplitRequest.mActivateTaskId == taskInfo.taskId) {
+ return mSplitRequest.mActivatePosition;
+ }
+ final String packageName1 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent);
+ final String basePackageName = SplitScreenUtils.getPackageName(taskInfo.baseIntent);
+ if (packageName1 != null && packageName1.equals(basePackageName)) {
+ return mSplitRequest.mActivatePosition;
+ }
+ final String packageName2 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent2);
+ if (packageName2 != null && packageName2.equals(basePackageName)) {
+ return mSplitRequest.mActivatePosition;
+ }
+ return SPLIT_POSITION_UNDEFINED;
+ }
+
/** Synchronize split-screen state with transition and make appropriate preparations. */
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,