Support launching a shortcut and a task to split screen

Bug: 243101552
Test: long press on a shortcut to enter split screen works
Change-Id: Ifc7f6ce926d45c9c13407921b80ab7b07d270cf1
This commit is contained in:
Jerry Chang
2022-09-01 13:35:11 +00:00
parent 21688acee8
commit 24158716b4
3 changed files with 105 additions and 84 deletions

View File

@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.view.RemoteAnimationAdapter;
@@ -89,7 +90,7 @@ interface ISplitScreen {
float splitRatio, in RemoteAnimationAdapter adapter) = 11;
/**
* Start a pair of intent and task using legacy transition system.
* Starts a pair of intent and task using legacy transition system.
*/
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions,
@@ -108,4 +109,11 @@ interface ISplitScreen {
* does not expect split to currently be running.
*/
RemoteAnimationTarget[] onStartingSplitLegacy(in RemoteAnimationTarget[] appTargets) = 14;
/**
* Starts a pair of shortcut and task using legacy transition system.
*/
oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, int taskId,
in Bundle mainOptions, in Bundle sideOptions, int sidePosition, float splitRatio,
in RemoteAnimationAdapter adapter) = 15;
}

View File

@@ -40,6 +40,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
@@ -787,6 +788,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
sidePosition, splitRatio, adapter));
}
@Override
public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
executeRemoteCallWithTaskPermission(mController,
"startShortcutAndTaskWithLegacyTransition", (controller) ->
controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
shortcutInfo, taskId, mainOptions, sideOptions, sidePosition,
splitRatio, adapter));
}
@Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,

View File

@@ -69,11 +69,11 @@ import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
@@ -85,7 +85,6 @@ import android.util.Log;
import android.util.Slog;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -531,133 +530,135 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
float splitRatio, RemoteAnimationAdapter adapter) {
startWithLegacyTransition(mainTaskId, sideTaskId, null /* pendingIntent */,
null /* fillInIntent */, mainOptions, sideOptions, sidePosition, splitRatio,
adapter);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.startTask(sideTaskId, sideOptions);
startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter);
}
/** Start an intent and a task ordered by {@code intentFirst}. */
void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
startWithLegacyTransition(taskId, INVALID_TASK_ID, pendingIntent, fillInIntent,
mainOptions, sideOptions, sidePosition, splitRatio, adapter);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
}
private void startWithLegacyTransition(int mainTaskId, int sideTaskId,
@Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
final boolean withIntent = pendingIntent != null && fillInIntent != null;
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, sideOptions);
startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
}
private void startWithLegacyTransition(WindowContainerTransaction sideWct, int mainTaskId,
@Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
RemoteAnimationAdapter adapter) {
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
mSplitLayout.setDivideRatio(splitRatio);
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true;
final WindowContainerTransaction wct = new WindowContainerTransaction();
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
// Need to add another wrapper here in shell so that we can inject the divider bar
// and also manage the process elevation via setRunningRemote
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
@Override
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
RemoteAnimationTarget[] augmentedNonApps =
new RemoteAnimationTarget[nonApps.length + 1];
for (int i = 0; i < nonApps.length; ++i) {
augmentedNonApps[i] = nonApps[i];
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
if (apps == null || apps.length == 0) {
onRemoteAnimationFinished(apps);
t.apply();
try {
adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
return;
}
augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
// The surfaces of splitting tasks were placed with window bounds when preparing the
// transition, so update divider surface separately.
final RemoteAnimationTarget dividerTarget = getDividerBarLegacyTarget();
mSplitLayout.getRefDividerBounds(mTempRect1);
t.setLayer(dividerTarget.leash, Integer.MAX_VALUE)
.setPosition(dividerTarget.leash, mTempRect1.left, mTempRect1.top);
setDividerVisibility(true, t);
for (int i = 0; i < apps.length; ++i) {
if (apps[i].mode == MODE_OPENING) {
t.show(apps[i].leash);
}
}
t.apply();
IRemoteAnimationFinishedCallback wrapCallback =
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
onRemoteAnimationFinished(apps);
finishedCallback.onAnimationFinished();
}
};
Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
try {
adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
augmentedNonApps, wrapCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
}
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
try {
adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
dividerTarget), wrapCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
}
};
RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
if (mainOptions == null) {
mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
} else {
ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
mainOptions = mainActivityOptions.toBundle();
}
sideOptions = sideOptions != null ? sideOptions : new Bundle();
final WindowContainerTransaction wct = new WindowContainerTransaction();
setSideStagePosition(sidePosition, wct);
mSplitLayout.setDivideRatio(splitRatio);
if (!mMainStage.isActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(wct, false /* reparent */);
}
if (mainOptions == null) mainOptions = new Bundle();
addActivityOptions(mainOptions, mMainStage);
wct.startTask(mainTaskId, mainOptions);
wct.merge(sideWct, true);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
wct.setForceTranslucent(mRootTaskInfo.token, false);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
// Add task launch requests
wct.startTask(mainTaskId, mainOptions);
if (withIntent) {
wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
} else {
wct.startTask(sideTaskId, sideOptions);
}
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t);
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
});
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
WindowContainerTransaction evictWct) {
private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
// 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.
if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
mMainExecutor.execute(() ->
exitSplitScreen(mMainStage.getChildCount() == 0
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
} else {
mSyncQueue.queue(evictWct);
if (apps == null || apps.length == 0) return;
// If any stage has no child after finished animation, that side of the split will display
// nothing. This might happen if starting the same app on the both sides while not
// supporting multi-instance. Exit the split screen and expand that app to full screen.
if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
return;
}
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
mSyncQueue.queue(evictWct);
}
/**