Merge "Support launching a shortcut and a task to split screen" into tm-qpr-dev

This commit is contained in:
Jerry Chang
2022-09-02 01:40:47 +00:00
committed by Android (Google) Code Review
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.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationAdapter;
@@ -89,7 +90,7 @@ interface ISplitScreen {
float splitRatio, in RemoteAnimationAdapter adapter) = 11; 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, oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions, 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. * does not expect split to currently be running.
*/ */
RemoteAnimationTarget[] onStartingSplitLegacy(in RemoteAnimationTarget[] appTargets) = 14; 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.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.LauncherApps; import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
@@ -787,6 +788,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
sidePosition, splitRatio, adapter)); 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 @Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions, public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, int sideTaskId, @Nullable Bundle sideOptions,

View File

@@ -69,11 +69,11 @@ import android.annotation.CallSuper;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.WindowConfiguration; import android.app.WindowConfiguration;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Rect; import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManager;
@@ -85,7 +85,6 @@ import android.util.Log;
import android.util.Slog; import android.util.Slog;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget; import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl; import android.view.SurfaceControl;
@@ -531,133 +530,135 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions, void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
float splitRatio, RemoteAnimationAdapter adapter) { float splitRatio, RemoteAnimationAdapter adapter) {
startWithLegacyTransition(mainTaskId, sideTaskId, null /* pendingIntent */, final WindowContainerTransaction wct = new WindowContainerTransaction();
null /* fillInIntent */, mainOptions, sideOptions, sidePosition, splitRatio, if (sideOptions == null) sideOptions = new Bundle();
adapter); addActivityOptions(sideOptions, mSideStage);
wct.startTask(sideTaskId, sideOptions);
startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter);
} }
/** Start an intent and a task ordered by {@code intentFirst}. */ /** Start an intent and a task ordered by {@code intentFirst}. */
void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions, int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) { @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
startWithLegacyTransition(taskId, INVALID_TASK_ID, pendingIntent, fillInIntent, final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions, sideOptions, sidePosition, splitRatio, adapter); 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, void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent, int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) { @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. // Init divider first to make divider leash for remote animation target.
mSplitLayout.init(); mSplitLayout.init();
mSplitLayout.setDivideRatio(splitRatio);
// Set false to avoid record new bounds with old task still on top; // Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false; mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true; mIsDividerRemoteAnimating = true;
final WindowContainerTransaction wct = new WindowContainerTransaction();
final WindowContainerTransaction evictWct = new WindowContainerTransaction(); LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
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() {
@Override @Override
public void onAnimationStart(@WindowManager.TransitionOldType int transit, public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
RemoteAnimationTarget[] wallpapers, IRemoteAnimationFinishedCallback finishedCallback,
RemoteAnimationTarget[] nonApps, SurfaceControl.Transaction t) {
final IRemoteAnimationFinishedCallback finishedCallback) { if (apps == null || apps.length == 0) {
RemoteAnimationTarget[] augmentedNonApps = onRemoteAnimationFinished(apps);
new RemoteAnimationTarget[nonApps.length + 1]; t.apply();
for (int i = 0; i < nonApps.length; ++i) { try {
augmentedNonApps[i] = nonApps[i]; 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 = IRemoteAnimationFinishedCallback wrapCallback =
new IRemoteAnimationFinishedCallback.Stub() { new IRemoteAnimationFinishedCallback.Stub() {
@Override @Override
public void onAnimationFinished() throws RemoteException { public void onAnimationFinished() throws RemoteException {
onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct); onRemoteAnimationFinished(apps);
finishedCallback.onAnimationFinished(); finishedCallback.onAnimationFinished();
} }
}; };
Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication()); Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
try { try {
adapter.getRunner().onAnimationStart(transit, apps, wallpapers, adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
augmentedNonApps, wrapCallback); ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
} catch (RemoteException e) { dividerTarget), wrapCallback);
Slog.e(TAG, "Error starting remote animation", e);
}
}
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
try {
adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e); Slog.e(TAG, "Error starting remote animation", e);
} }
} }
}; };
RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
if (mainOptions == null) { final WindowContainerTransaction wct = new WindowContainerTransaction();
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();
setSideStagePosition(sidePosition, wct); setSideStagePosition(sidePosition, wct);
mSplitLayout.setDivideRatio(splitRatio);
if (!mMainStage.isActive()) { 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 */); 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); updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true); wct.reorder(mRootTaskInfo.token, true);
wct.setForceTranslucent(mRootTaskInfo.token, false); wct.setForceTranslucent(mRootTaskInfo.token, false);
// Make sure the launch options will put tasks in the corresponding split roots mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
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 */);
});
} }
private void onRemoteAnimationFinishedOrCancelled(boolean cancel, private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
WindowContainerTransaction evictWct) {
mIsDividerRemoteAnimating = false; mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true; mShouldUpdateRecents = true;
// If any stage has no child after animation finished, it means that split will display if (apps == null || apps.length == 0) return;
// 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 any stage has no child after finished animation, that side of the split will display
if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) { // nothing. This might happen if starting the same app on the both sides while not
mMainExecutor.execute(() -> // supporting multi-instance. Exit the split screen and expand that app to full screen.
exitSplitScreen(mMainStage.getChildCount() == 0 if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
} else { ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
mSyncQueue.queue(evictWct); 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);
} }
/** /**