Merge changes I710805c8,Ie26d634b into sc-v2-dev
* changes: Create a LegacyTransitions to manage combined WCT + remoteanimation Aggregate stage-split operations into WCT
This commit is contained in:
@@ -646,4 +646,9 @@ public abstract class ActivityManagerInternal {
|
||||
*/
|
||||
@Nullable
|
||||
public abstract List<Integer> getIsolatedProcesses(int uid);
|
||||
|
||||
/** @see ActivityManagerService#sendIntentSender */
|
||||
public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
|
||||
Intent intent, String resolvedType,
|
||||
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.window;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.window.IDisplayAreaOrganizerController;
|
||||
import android.window.ITaskFragmentOrganizerController;
|
||||
import android.window.ITaskOrganizerController;
|
||||
@@ -60,6 +61,17 @@ interface IWindowOrganizerController {
|
||||
IBinder startTransition(int type, in @nullable IBinder transitionToken,
|
||||
in @nullable WindowContainerTransaction t);
|
||||
|
||||
/**
|
||||
* Starts a legacy transition.
|
||||
* @param type The transition type.
|
||||
* @param adapter The animation to use.
|
||||
* @param syncCallback A sync callback for the contents of `t`
|
||||
* @param t Operations that are part of the transition.
|
||||
* @return sync-id or -1 if this no-op'd because a transition is already running.
|
||||
*/
|
||||
int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
|
||||
in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
|
||||
|
||||
/**
|
||||
* Finishes a transition. This must be called for all created transitions.
|
||||
* @param transitionToken Which transition to finish
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.window;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -388,6 +389,24 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a pending intent in sync.
|
||||
* @param sender The PendingIntent sender.
|
||||
* @param intent The fillIn intent to patch over the sender's base intent.
|
||||
* @param options bundle containing ActivityOptions for the task's top activity.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public WindowContainerTransaction sendPendingIntent(PendingIntent sender, Intent intent,
|
||||
@Nullable Bundle options) {
|
||||
mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
|
||||
.setLaunchOptions(options)
|
||||
.setPendingIntent(sender)
|
||||
.setActivityIntent(intent)
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new TaskFragment with the given options.
|
||||
* @param taskFragmentOptions the options used to create the TaskFragment.
|
||||
@@ -886,6 +905,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9;
|
||||
public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10;
|
||||
public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
|
||||
public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
|
||||
|
||||
// The following key(s) are for use with mLaunchOptions:
|
||||
// When launching a task (eg. from recents), this is the taskId to be launched.
|
||||
@@ -920,6 +940,9 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
@Nullable
|
||||
private TaskFragmentCreationParams mTaskFragmentCreationOptions;
|
||||
|
||||
@Nullable
|
||||
private PendingIntent mPendingIntent;
|
||||
|
||||
public static HierarchyOp createForReparent(
|
||||
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
|
||||
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
|
||||
@@ -998,6 +1021,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
mLaunchOptions = copy.mLaunchOptions;
|
||||
mActivityIntent = copy.mActivityIntent;
|
||||
mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
|
||||
mPendingIntent = copy.mPendingIntent;
|
||||
}
|
||||
|
||||
protected HierarchyOp(Parcel in) {
|
||||
@@ -1010,6 +1034,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
mLaunchOptions = in.readBundle();
|
||||
mActivityIntent = in.readTypedObject(Intent.CREATOR);
|
||||
mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
|
||||
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
@@ -1062,6 +1087,11 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return mTaskFragmentCreationOptions;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PendingIntent getPendingIntent() {
|
||||
return mPendingIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (mType) {
|
||||
@@ -1117,6 +1147,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
dest.writeBundle(mLaunchOptions);
|
||||
dest.writeTypedObject(mActivityIntent, flags);
|
||||
dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
|
||||
dest.writeTypedObject(mPendingIntent, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1163,6 +1194,9 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
@Nullable
|
||||
private TaskFragmentCreationParams mTaskFragmentCreationOptions;
|
||||
|
||||
@Nullable
|
||||
private PendingIntent mPendingIntent;
|
||||
|
||||
Builder(int type) {
|
||||
mType = type;
|
||||
}
|
||||
@@ -1202,6 +1236,11 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setPendingIntent(@Nullable PendingIntent sender) {
|
||||
mPendingIntent = sender;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setTaskFragmentCreationOptions(
|
||||
@Nullable TaskFragmentCreationParams taskFragmentCreationOptions) {
|
||||
mTaskFragmentCreationOptions = taskFragmentCreationOptions;
|
||||
@@ -1221,6 +1260,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
hierarchyOp.mToTop = mToTop;
|
||||
hierarchyOp.mLaunchOptions = mLaunchOptions;
|
||||
hierarchyOp.mActivityIntent = mActivityIntent;
|
||||
hierarchyOp.mPendingIntent = mPendingIntent;
|
||||
hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
|
||||
|
||||
return hierarchyOp;
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.app.ActivityTaskManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Singleton;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
|
||||
/**
|
||||
* Base class for organizing specific types of windows like Tasks and DisplayAreas
|
||||
@@ -124,6 +125,27 @@ public class WindowOrganizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a legacy transition.
|
||||
* @param type The type of the transition. This is ignored if a transitionToken is provided.
|
||||
* @param adapter An existing transition to start. If null, a new transition is created.
|
||||
* @param t The set of window operations that are part of this transition.
|
||||
* @return true on success, false if a transition was already running.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
||||
@NonNull
|
||||
public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
|
||||
@NonNull WindowContainerTransactionCallback syncCallback,
|
||||
@NonNull WindowContainerTransaction t) {
|
||||
try {
|
||||
return getWindowOrganizerController().startLegacyTransition(
|
||||
type, adapter, syncCallback.mInterface, t);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an ITransitionPlayer to handle transition animations.
|
||||
* @hide
|
||||
|
||||
@@ -187,6 +187,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
|
||||
.setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
|
||||
mTaskInfo2.positionInParent.y)
|
||||
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
|
||||
.show(dividerLeash)
|
||||
.show(mRootTaskLeash)
|
||||
.show(mTaskLeash1)
|
||||
.show(mTaskLeash2);
|
||||
@@ -212,9 +213,12 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
|
||||
}
|
||||
mRootTaskInfo = taskInfo;
|
||||
|
||||
if (mSplitLayout != null
|
||||
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
|
||||
onBoundsChanged(mSplitLayout);
|
||||
if (mSplitLayout != null) {
|
||||
if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
|
||||
onBoundsChanged(mSplitLayout);
|
||||
}
|
||||
// updateConfiguration re-inits the dividerbar, so show it now
|
||||
mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash()));
|
||||
}
|
||||
} else if (taskInfo.taskId == getTaskId1()) {
|
||||
mTaskInfo1 = taskInfo;
|
||||
|
||||
@@ -18,13 +18,15 @@ package com.android.wm.shell.common;
|
||||
|
||||
import android.annotation.BinderThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.WindowContainerTransaction;
|
||||
import android.window.WindowContainerTransactionCallback;
|
||||
import android.window.WindowOrganizer;
|
||||
|
||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||
import com.android.wm.shell.transition.LegacyTransitions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -76,6 +78,21 @@ public final class SyncTransactionQueue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a legacy transition to be sent serially to WM
|
||||
*/
|
||||
public void queue(LegacyTransitions.ILegacyTransition transition,
|
||||
@WindowManager.TransitionType int type, WindowContainerTransaction wct) {
|
||||
SyncCallback cb = new SyncCallback(transition, type, wct);
|
||||
synchronized (mQueue) {
|
||||
if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
|
||||
mQueue.add(cb);
|
||||
if (mQueue.size() == 1) {
|
||||
cb.send();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
|
||||
* Otherwise just returns without queueing.
|
||||
@@ -118,12 +135,12 @@ public final class SyncTransactionQueue {
|
||||
// Synchronized on mQueue
|
||||
private void onTransactionReceived(@NonNull SurfaceControl.Transaction t) {
|
||||
if (DEBUG) Slog.d(TAG, " Running " + mRunnables.size() + " sync runnables");
|
||||
for (int i = 0, n = mRunnables.size(); i < n; ++i) {
|
||||
final int n = mRunnables.size();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
mRunnables.get(i).runWithTransaction(t);
|
||||
}
|
||||
mRunnables.clear();
|
||||
t.apply();
|
||||
t.close();
|
||||
// More runnables may have been added, so only remove the ones that ran.
|
||||
mRunnables.subList(0, n).clear();
|
||||
}
|
||||
|
||||
/** Task to run with transaction. */
|
||||
@@ -135,20 +152,38 @@ public final class SyncTransactionQueue {
|
||||
private class SyncCallback extends WindowContainerTransactionCallback {
|
||||
int mId = -1;
|
||||
final WindowContainerTransaction mWCT;
|
||||
final LegacyTransitions.LegacyTransition mLegacyTransition;
|
||||
|
||||
SyncCallback(WindowContainerTransaction wct) {
|
||||
mWCT = wct;
|
||||
mLegacyTransition = null;
|
||||
}
|
||||
|
||||
SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
|
||||
@WindowManager.TransitionType int type, WindowContainerTransaction wct) {
|
||||
mWCT = wct;
|
||||
mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
|
||||
}
|
||||
|
||||
// Must be sychronized on mQueue
|
||||
void send() {
|
||||
if (mInFlight == this) {
|
||||
// This was probably queued up and sent during a sync runnable of the last callback.
|
||||
// Don't queue it again.
|
||||
return;
|
||||
}
|
||||
if (mInFlight != null) {
|
||||
throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
|
||||
+ mInFlight.mId + " - " + mInFlight.mWCT);
|
||||
}
|
||||
mInFlight = this;
|
||||
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
|
||||
mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
|
||||
if (mLegacyTransition != null) {
|
||||
mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
|
||||
mLegacyTransition.getAdapter(), this, mWCT);
|
||||
} else {
|
||||
mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
|
||||
}
|
||||
if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
|
||||
mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
|
||||
}
|
||||
@@ -169,6 +204,16 @@ public final class SyncTransactionQueue {
|
||||
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
|
||||
mQueue.remove(this);
|
||||
onTransactionReceived(t);
|
||||
if (mLegacyTransition != null) {
|
||||
try {
|
||||
mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
|
||||
}
|
||||
} else {
|
||||
t.apply();
|
||||
t.close();
|
||||
}
|
||||
if (!mQueue.isEmpty()) {
|
||||
mQueue.get(0).send();
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
|
||||
.setContainerLayer()
|
||||
.setName(TAG)
|
||||
.setHidden(false)
|
||||
.setHidden(true)
|
||||
.setCallsite("SplitWindowManager#attachToParentSurface");
|
||||
mParentContainerCallbacks.attachToParentSurface(builder);
|
||||
mLeash = builder.build();
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||
|
||||
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
@@ -39,9 +37,13 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Slog;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.IRemoteTransition;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -58,6 +60,7 @@ import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
|
||||
import com.android.wm.shell.splitscreen.ISplitScreenListener;
|
||||
import com.android.wm.shell.transition.LegacyTransitions;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -147,7 +150,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
|
||||
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
||||
mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
|
||||
}
|
||||
|
||||
public void setSideStageVisibility(boolean visible) {
|
||||
@@ -185,7 +188,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
|
||||
public void startTask(int taskId, @SplitScreen.StageType int stage,
|
||||
@SplitPosition int position, @Nullable Bundle options) {
|
||||
options = resolveStartStage(stage, position, options);
|
||||
options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
|
||||
|
||||
try {
|
||||
ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
|
||||
@@ -197,7 +200,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
public void startShortcut(String packageName, String shortcutId,
|
||||
@SplitScreen.StageType int stage, @SplitPosition int position,
|
||||
@Nullable Bundle options, UserHandle user) {
|
||||
options = resolveStartStage(stage, position, options);
|
||||
options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
|
||||
|
||||
try {
|
||||
LauncherApps launcherApps =
|
||||
@@ -212,64 +215,61 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
public void startIntent(PendingIntent intent, Intent fillInIntent,
|
||||
@SplitScreen.StageType int stage, @SplitPosition int position,
|
||||
@Nullable Bundle options) {
|
||||
options = resolveStartStage(stage, position, options);
|
||||
|
||||
try {
|
||||
intent.send(mContext, 0, fillInIntent, null, null, null, options);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Slog.e(TAG, "Failed to launch activity", e);
|
||||
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
|
||||
startIntentLegacy(intent, fillInIntent, stage, position, options);
|
||||
return;
|
||||
}
|
||||
mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options,
|
||||
null /* remote */);
|
||||
}
|
||||
|
||||
private Bundle resolveStartStage(@SplitScreen.StageType int stage,
|
||||
@SplitPosition int position, @Nullable Bundle options) {
|
||||
switch (stage) {
|
||||
case STAGE_TYPE_UNDEFINED: {
|
||||
// Use the stage of the specified position is valid.
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
if (position == mStageCoordinator.getSideStagePosition()) {
|
||||
options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
|
||||
} else {
|
||||
options = resolveStartStage(STAGE_TYPE_MAIN, position, options);
|
||||
}
|
||||
} else {
|
||||
// Exit split-screen and launch fullscreen since stage wasn't specified.
|
||||
mStageCoordinator.exitSplitScreen();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STAGE_TYPE_SIDE: {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
mStageCoordinator.setSideStagePosition(position);
|
||||
} else {
|
||||
position = mStageCoordinator.getSideStagePosition();
|
||||
}
|
||||
if (options == null) {
|
||||
options = new Bundle();
|
||||
}
|
||||
mStageCoordinator.updateActivityOptions(options, position);
|
||||
break;
|
||||
}
|
||||
case STAGE_TYPE_MAIN: {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
// Set the side stage opposite of what we want to the main stage.
|
||||
final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||
mStageCoordinator.setSideStagePosition(sideStagePosition);
|
||||
} else {
|
||||
position = mStageCoordinator.getMainStagePosition();
|
||||
}
|
||||
if (options == null) {
|
||||
options = new Bundle();
|
||||
}
|
||||
mStageCoordinator.updateActivityOptions(options, position);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown stage=" + stage);
|
||||
}
|
||||
private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
|
||||
@SplitScreen.StageType int stage, @SplitPosition int position,
|
||||
@Nullable Bundle options) {
|
||||
final boolean wasInSplit = isSplitScreenVisible();
|
||||
|
||||
return options;
|
||||
LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
|
||||
@Override
|
||||
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback,
|
||||
SurfaceControl.Transaction t) {
|
||||
boolean cancelled = apps == null || apps.length == 0;
|
||||
mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
|
||||
if (cancelled) {
|
||||
if (!wasInSplit) {
|
||||
final WindowContainerTransaction undoWct = new WindowContainerTransaction();
|
||||
mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, undoWct);
|
||||
mSyncQueue.queue(undoWct);
|
||||
mSyncQueue.runInSync(undoT -> {
|
||||
// looks weird, but we want undoT to execute after t but still want the
|
||||
// rest of the syncQueue runnables to aggregate.
|
||||
t.merge(undoT);
|
||||
undoT.merge(t);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < apps.length; ++i) {
|
||||
if (apps[i].mode == MODE_OPENING) {
|
||||
t.show(apps[i].leash);
|
||||
}
|
||||
}
|
||||
RemoteAnimationTarget divider = mStageCoordinator.getDividerBarLegacyTarget();
|
||||
t.show(divider.leash);
|
||||
t.apply();
|
||||
if (cancelled) return;
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error finishing legacy transition: ", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
|
||||
wct.sendPendingIntent(intent, fillInIntent, options);
|
||||
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
|
||||
}
|
||||
|
||||
RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
|
||||
|
||||
@@ -36,6 +36,7 @@ import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
|
||||
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
|
||||
import static com.android.wm.shell.transition.Transitions.isClosingType;
|
||||
import static com.android.wm.shell.transition.Transitions.isOpeningType;
|
||||
@@ -45,8 +46,10 @@ import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.os.Bundle;
|
||||
@@ -211,7 +214,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||
@SplitPosition int sideStagePosition) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
setSideStagePosition(sideStagePosition);
|
||||
setSideStagePosition(sideStagePosition, wct);
|
||||
mMainStage.activate(getMainStageBounds(), wct);
|
||||
mSideStage.addTask(task, getSideStageBounds(), wct);
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
@@ -243,7 +246,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
mainOptions = mainOptions != null ? mainOptions : new Bundle();
|
||||
sideOptions = sideOptions != null ? sideOptions : new Bundle();
|
||||
setSideStagePosition(sidePosition);
|
||||
setSideStagePosition(sidePosition, wct);
|
||||
|
||||
// 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.
|
||||
@@ -312,7 +315,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
}
|
||||
|
||||
sideOptions = sideOptions != null ? sideOptions : new Bundle();
|
||||
setSideStagePosition(sidePosition);
|
||||
setSideStagePosition(sidePosition, wct);
|
||||
|
||||
// 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.
|
||||
@@ -331,6 +334,69 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
}
|
||||
|
||||
public void startIntent(PendingIntent intent, Intent fillInIntent,
|
||||
@SplitScreen.StageType int stage, @SplitPosition int position,
|
||||
@androidx.annotation.Nullable Bundle options,
|
||||
@Nullable IRemoteTransition remoteTransition) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
options = resolveStartStage(stage, position, options, wct);
|
||||
wct.sendPendingIntent(intent, fillInIntent, options);
|
||||
mSplitTransitions.startEnterTransition(
|
||||
TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
|
||||
}
|
||||
|
||||
Bundle resolveStartStage(@SplitScreen.StageType int stage,
|
||||
@SplitPosition int position, @androidx.annotation.Nullable Bundle options,
|
||||
@androidx.annotation.Nullable WindowContainerTransaction wct) {
|
||||
switch (stage) {
|
||||
case STAGE_TYPE_UNDEFINED: {
|
||||
// Use the stage of the specified position is valid.
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
if (position == getSideStagePosition()) {
|
||||
options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
|
||||
} else {
|
||||
options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct);
|
||||
}
|
||||
} else {
|
||||
// Exit split-screen and launch fullscreen since stage wasn't specified.
|
||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STAGE_TYPE_SIDE: {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
setSideStagePosition(position, wct);
|
||||
} else {
|
||||
position = getSideStagePosition();
|
||||
}
|
||||
if (options == null) {
|
||||
options = new Bundle();
|
||||
}
|
||||
updateActivityOptions(options, position, wct);
|
||||
break;
|
||||
}
|
||||
case STAGE_TYPE_MAIN: {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
// Set the side stage opposite of what we want to the main stage.
|
||||
final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||
setSideStagePosition(sideStagePosition, wct);
|
||||
} else {
|
||||
position = getMainStagePosition();
|
||||
}
|
||||
if (options == null) {
|
||||
options = new Bundle();
|
||||
}
|
||||
updateActivityOptions(options, position, wct);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown stage=" + stage);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@SplitLayout.SplitPosition
|
||||
int getSideStagePosition() {
|
||||
return mSideStagePosition;
|
||||
@@ -342,18 +408,24 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||
}
|
||||
|
||||
void setSideStagePosition(@SplitPosition int sideStagePosition) {
|
||||
setSideStagePosition(sideStagePosition, true /* updateBounds */);
|
||||
void setSideStagePosition(@SplitPosition int sideStagePosition,
|
||||
@Nullable WindowContainerTransaction wct) {
|
||||
setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
|
||||
}
|
||||
|
||||
private void setSideStagePosition(@SplitPosition int sideStagePosition,
|
||||
boolean updateBounds) {
|
||||
boolean updateBounds, @Nullable WindowContainerTransaction wct) {
|
||||
if (mSideStagePosition == sideStagePosition) return;
|
||||
mSideStagePosition = sideStagePosition;
|
||||
sendOnStagePositionChanged();
|
||||
|
||||
if (mSideStageListener.mVisible && updateBounds) {
|
||||
onBoundsChanged(mSplitLayout);
|
||||
if (wct == null) {
|
||||
// onBoundsChanged builds/applies a wct with the contents of updateWindowBounds.
|
||||
onBoundsChanged(mSplitLayout);
|
||||
} else {
|
||||
updateWindowBounds(mSplitLayout, wct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +460,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSplitLayout.resetDividerPosition();
|
||||
}
|
||||
|
||||
private void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
|
||||
/**
|
||||
* Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates
|
||||
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
|
||||
* to be used when exiting split might be bundled with other window operations.
|
||||
*/
|
||||
void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
|
||||
@NonNull WindowContainerTransaction wct) {
|
||||
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
|
||||
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
|
||||
@@ -403,15 +480,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
|
||||
}
|
||||
|
||||
void updateActivityOptions(Bundle opts, @SplitPosition int position) {
|
||||
void updateActivityOptions(Bundle opts, @SplitPosition int position,
|
||||
@Nullable WindowContainerTransaction wct) {
|
||||
addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
|
||||
|
||||
if (!mMainStage.isActive()) {
|
||||
// Activate the main stage in anticipation of an app launch.
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
boolean needsApply = wct == null;
|
||||
if (needsApply) {
|
||||
wct = new WindowContainerTransaction();
|
||||
}
|
||||
mMainStage.activate(getMainStageBounds(), wct);
|
||||
mSideStage.setBounds(getSideStageBounds(), wct);
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
if (needsApply) {
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,32 +707,41 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
@Override
|
||||
public void onDoubleTappedDivider() {
|
||||
setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT);
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBoundsChanging(SplitLayout layout) {
|
||||
final StageTaskListener topLeftStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
||||
final StageTaskListener bottomRightStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
|
||||
|
||||
mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
|
||||
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
|
||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBoundsChanged(SplitLayout layout) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
updateWindowBounds(layout, wct);
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates `wct` with operations that match the split windows to the current layout.
|
||||
* To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
|
||||
*/
|
||||
private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) {
|
||||
final StageTaskListener topLeftStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
||||
final StageTaskListener bottomRightStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
|
||||
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
|
||||
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
|
||||
}
|
||||
|
||||
void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
|
||||
final StageTaskListener topLeftStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
||||
final StageTaskListener bottomRightStage =
|
||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
|
||||
(layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
|
||||
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -869,7 +961,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
// Update local states (before animating).
|
||||
setDividerVisibility(true);
|
||||
setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */);
|
||||
setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
|
||||
null /* wct */);
|
||||
setSplitsVisible(true);
|
||||
|
||||
addDividerBarToTransition(info, t, true /* show */);
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.transition;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.RemoteException;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.IWindowContainerTransactionCallback;
|
||||
|
||||
/**
|
||||
* Utilities and interfaces for transition-like usage on top of the legacy app-transition and
|
||||
* synctransaction tools.
|
||||
*/
|
||||
public class LegacyTransitions {
|
||||
|
||||
/**
|
||||
* Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
|
||||
* into one callback.
|
||||
*/
|
||||
public interface ILegacyTransition {
|
||||
/**
|
||||
* Called when both the associated sync transaction finishes and the remote animation is
|
||||
* ready.
|
||||
*/
|
||||
void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that a remote animation and corresponding sync callback are called together
|
||||
* such that the sync callback is called first. This assumes that both the callback receiver
|
||||
* and the remoteanimation are in the same process so that order is preserved on both ends.
|
||||
*/
|
||||
public static class LegacyTransition {
|
||||
private final ILegacyTransition mLegacyTransition;
|
||||
private int mSyncId = -1;
|
||||
private SurfaceControl.Transaction mTransaction;
|
||||
private int mTransit;
|
||||
private RemoteAnimationTarget[] mApps;
|
||||
private RemoteAnimationTarget[] mWallpapers;
|
||||
private RemoteAnimationTarget[] mNonApps;
|
||||
private IRemoteAnimationFinishedCallback mFinishCallback = null;
|
||||
private boolean mCancelled = false;
|
||||
private final SyncCallback mSyncCallback = new SyncCallback();
|
||||
private final RemoteAnimationAdapter mAdapter =
|
||||
new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
|
||||
|
||||
public LegacyTransition(@WindowManager.TransitionType int type,
|
||||
@NonNull ILegacyTransition legacyTransition) {
|
||||
mLegacyTransition = legacyTransition;
|
||||
mTransit = type;
|
||||
}
|
||||
|
||||
public @WindowManager.TransitionType int getType() {
|
||||
return mTransit;
|
||||
}
|
||||
|
||||
public IWindowContainerTransactionCallback getSyncCallback() {
|
||||
return mSyncCallback;
|
||||
}
|
||||
|
||||
public RemoteAnimationAdapter getAdapter() {
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
|
||||
@Override
|
||||
public void onTransactionReady(int id, SurfaceControl.Transaction t)
|
||||
throws RemoteException {
|
||||
mSyncId = id;
|
||||
mTransaction = t;
|
||||
checkApply();
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
|
||||
@Override
|
||||
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
|
||||
mTransit = transit;
|
||||
mApps = apps;
|
||||
mWallpapers = wallpapers;
|
||||
mNonApps = nonApps;
|
||||
mFinishCallback = finishedCallback;
|
||||
checkApply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancelled() throws RemoteException {
|
||||
mCancelled = true;
|
||||
mApps = mWallpapers = mNonApps = null;
|
||||
checkApply();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkApply() throws RemoteException {
|
||||
if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) return;
|
||||
mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
|
||||
mNonApps, mFinishCallback, mTransaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,9 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
/** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
|
||||
public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
|
||||
|
||||
/** Transition type for entering split by opening an app into side-stage. */
|
||||
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
|
||||
|
||||
private final WindowOrganizer mOrganizer;
|
||||
private final Context mContext;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
|
||||
@@ -16348,6 +16348,15 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
return mProcessList.getIsolatedProcessesLocked(uid);
|
||||
}
|
||||
}
|
||||
|
||||
/** @see ActivityManagerService#sendIntentSender */
|
||||
@Override
|
||||
public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
|
||||
Intent intent, String resolvedType,
|
||||
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||
return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code,
|
||||
intent, resolvedType, finishedReceiver, requiredPermission, options);
|
||||
}
|
||||
}
|
||||
|
||||
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
|
||||
|
||||
@@ -213,6 +213,7 @@ public class AppTransition implements Dump {
|
||||
private int mNextAppTransitionEnter;
|
||||
private int mNextAppTransitionExit;
|
||||
private int mNextAppTransitionInPlace;
|
||||
private boolean mNextAppTransitionIsSync;
|
||||
|
||||
// Keyed by WindowContainer hashCode.
|
||||
private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
|
||||
@@ -348,6 +349,13 @@ public class AppTransition implements Dump {
|
||||
fetchAppTransitionSpecsFromFuture();
|
||||
}
|
||||
|
||||
void abort() {
|
||||
if (mRemoteAnimationController != null) {
|
||||
mRemoteAnimationController.cancelAnimation("aborted");
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
boolean isRunning() {
|
||||
return mAppTransitionState == APP_STATE_RUNNING;
|
||||
}
|
||||
@@ -466,6 +474,7 @@ public class AppTransition implements Dump {
|
||||
mNextAppTransitionAnimationsSpecsFuture = null;
|
||||
mDefaultNextAppTransitionAnimationSpec = null;
|
||||
mAnimationFinishedCallback = null;
|
||||
mNextAppTransitionIsSync = false;
|
||||
}
|
||||
|
||||
void freeze() {
|
||||
@@ -1146,13 +1155,19 @@ public class AppTransition implements Dump {
|
||||
}
|
||||
|
||||
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
|
||||
overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */);
|
||||
}
|
||||
|
||||
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
|
||||
boolean sync) {
|
||||
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
|
||||
isTransitionSet(), remoteAnimationAdapter);
|
||||
if (isTransitionSet()) {
|
||||
if (isTransitionSet() && !mNextAppTransitionIsSync) {
|
||||
clear();
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
|
||||
mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
|
||||
remoteAnimationAdapter, mHandler);
|
||||
mNextAppTransitionIsSync = sync;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5014,6 +5014,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
}
|
||||
|
||||
void cancelAppTransition() {
|
||||
if (!mAppTransition.isTransitionSet() || mAppTransition.isRunning()) return;
|
||||
mAppTransition.abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update pendingLayoutChanges after app transition has finished.
|
||||
*/
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
|
||||
@@ -39,6 +41,8 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -52,6 +56,7 @@ import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.IDisplayAreaOrganizerController;
|
||||
import android.window.ITaskFragmentOrganizer;
|
||||
@@ -237,6 +242,44 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
|
||||
@NonNull IWindowContainerTransactionCallback callback,
|
||||
@NonNull WindowContainerTransaction t) {
|
||||
enforceTaskPermission("startLegacyTransition()");
|
||||
final CallerInfo caller = new CallerInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
int syncId;
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
if (type < 0) {
|
||||
throw new IllegalArgumentException("Can't create transition with no type");
|
||||
}
|
||||
if (mTransitionController.getTransitionPlayer() != null) {
|
||||
throw new IllegalArgumentException("Can't use legacy transitions in"
|
||||
+ " when shell transitions are enabled.");
|
||||
}
|
||||
final DisplayContent dc =
|
||||
mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
|
||||
if (dc.mAppTransition.isTransitionSet()) {
|
||||
// a transition already exists, so the callback probably won't be called.
|
||||
return -1;
|
||||
}
|
||||
adapter.setCallingPidUid(caller.mPid, caller.mUid);
|
||||
dc.prepareAppTransition(type);
|
||||
dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */);
|
||||
syncId = startSyncWithOrganizer(callback);
|
||||
applyTransaction(t, syncId, null /* transition */, caller);
|
||||
setSyncReady(syncId);
|
||||
mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY)
|
||||
.executeAppTransition();
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
return syncId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int finishTransition(@NonNull IBinder transitionToken,
|
||||
@Nullable WindowContainerTransaction t,
|
||||
@@ -578,6 +621,37 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid,
|
||||
taskId, safeOptions);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_PENDING_INTENT:
|
||||
String resolvedType = hop.getActivityIntent() != null
|
||||
? hop.getActivityIntent().resolveTypeIfNeeded(
|
||||
mService.mContext.getContentResolver())
|
||||
: null;
|
||||
|
||||
Bundle options = null;
|
||||
if (hop.getPendingIntent().isActivity()) {
|
||||
// Set the context display id as preferred for this activity launches, so that
|
||||
// it can land on caller's display. Or just brought the task to front at the
|
||||
// display where it was on since it has higher preference.
|
||||
ActivityOptions activityOptions = hop.getLaunchOptions() != null
|
||||
? new ActivityOptions(hop.getLaunchOptions())
|
||||
: ActivityOptions.makeBasic();
|
||||
activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
|
||||
options = activityOptions.toBundle();
|
||||
}
|
||||
|
||||
int res = mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
|
||||
hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
|
||||
hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
|
||||
null /* requiredPermission */, options);
|
||||
if (res != ActivityManager.START_SUCCESS
|
||||
&& res != ActivityManager.START_TASK_TO_FRONT) {
|
||||
if (!mTransitionController.isShellTransitionsEnabled()) {
|
||||
final DisplayContent dc =
|
||||
mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
|
||||
dc.cancelAppTransition();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
|
||||
final TaskFragmentCreationParams taskFragmentCreationOptions =
|
||||
hop.getTaskFragmentCreationOptions();
|
||||
|
||||
Reference in New Issue
Block a user