Merge "Support launching a shortcut and a task to split screen" into tm-qpr-dev
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
|
||||||
mSyncQueue.runInSync(t -> {
|
|
||||||
setDividerVisibility(true, t);
|
|
||||||
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
|
|
||||||
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) {
|
||||||
|
mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
|
||||||
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
|
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
|
||||||
} else {
|
return;
|
||||||
mSyncQueue.queue(evictWct);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||||
|
prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
|
||||||
|
prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
|
||||||
|
mSyncQueue.queue(evictWct);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user