diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java index 1c40a06c1c86..42fb24f570d2 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java @@ -217,7 +217,7 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase } @Override - public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException { + public void onTasksAppeared(RemoteAnimationTarget[] app) throws RemoteException { /* no-op */ } }; diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl index aca17e448b82..c7fd38092ec7 100644 --- a/core/java/android/view/IRecentsAnimationRunner.aidl +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -63,5 +63,5 @@ oneway interface IRecentsAnimationRunner { * Called when the task of an activity that has been started while the recents animation * was running becomes ready for control. */ - void onTaskAppeared(in RemoteAnimationTarget app) = 3; + void onTasksAppeared(in RemoteAnimationTarget[] app) = 3; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index b95123d2fa41..38eded878014 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -195,8 +195,13 @@ public class ActivityManagerWrapper { } @Override - public void onTaskAppeared(RemoteAnimationTarget app) { - animationHandler.onTaskAppeared(new RemoteAnimationTargetCompat(app)); + public void onTasksAppeared(RemoteAnimationTarget[] apps) { + final RemoteAnimationTargetCompat[] compats = + new RemoteAnimationTargetCompat[apps.length]; + for (int i = 0; i < apps.length; ++i) { + compats[i] = new RemoteAnimationTargetCompat(apps[i]); + } + animationHandler.onTasksAppeared(compats); } }; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java index a74de2e0c085..48f1b76c1d50 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java @@ -39,5 +39,5 @@ public interface RecentsAnimationListener { * Called when the task of an activity that has been started while the recents animation * was running becomes ready for control. */ - void onTaskAppeared(RemoteAnimationTargetCompat app); + void onTasksAppeared(RemoteAnimationTargetCompat[] app); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 99b6aed497cc..954cf9fd81a8 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -53,6 +53,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DataClass; import com.android.systemui.shared.recents.model.ThumbnailData; +import java.util.ArrayList; import java.util.concurrent.Executor; /** @@ -127,7 +128,7 @@ public class RemoteTransitionCompat implements Parcelable { mToken = transition; // This transition is for opening recents, so recents is on-top. We want to draw // the current going-away task on top of recents, though, so move it to front - WindowContainerToken pausingTask = null; + final ArrayList pausingTasks = new ArrayList<>(); WindowContainerToken pipTask = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -138,7 +139,8 @@ public class RemoteTransitionCompat implements Parcelable { if (taskInfo == null) { continue; } - pausingTask = taskInfo.token; + // Add to front since we are iterating backwards. + pausingTasks.add(0, taskInfo.token); if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { pipTask = taskInfo.token; @@ -150,7 +152,7 @@ public class RemoteTransitionCompat implements Parcelable { t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1); } t.apply(); - mRecentsSession.setup(controller, info, finishedCallback, pausingTask, pipTask, + mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask, leashMap, mToken); recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0), new Rect()); @@ -198,18 +200,18 @@ public class RemoteTransitionCompat implements Parcelable { static class RecentsControllerWrap extends RecentsAnimationControllerCompat { private RecentsAnimationControllerCompat mWrapped = null; private IRemoteTransitionFinishedCallback mFinishCB = null; - private WindowContainerToken mPausingTask = null; + private ArrayList mPausingTasks = null; private WindowContainerToken mPipTask = null; private TransitionInfo mInfo = null; - private SurfaceControl mOpeningLeash = null; + private ArrayList mOpeningLeashes = null; private ArrayMap mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; private IBinder mTransition = null; void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info, - IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask, - WindowContainerToken pipTask, ArrayMap leashMap, - IBinder transition) { + IRemoteTransitionFinishedCallback finishCB, + ArrayList pausingTasks, WindowContainerToken pipTask, + ArrayMap leashMap, IBinder transition) { if (mInfo != null) { throw new IllegalStateException("Trying to run a new recents animation while" + " recents is already active."); @@ -217,7 +219,7 @@ public class RemoteTransitionCompat implements Parcelable { mWrapped = wrapped; mInfo = info; mFinishCB = finishCB; - mPausingTask = pausingTask; + mPausingTasks = pausingTasks; mPipTask = pipTask; mLeashMap = leashMap; mTransition = transition; @@ -226,36 +228,57 @@ public class RemoteTransitionCompat implements Parcelable { @SuppressLint("NewApi") boolean merge(TransitionInfo info, SurfaceControl.Transaction t, RecentsAnimationListener recents) { - TransitionInfo.Change openingTask = null; + ArrayList openingTasks = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) { if (change.getTaskInfo() != null) { - if (openingTask != null) { - Log.w(TAG, " Expecting to merge a task-open, but got >1 opening " - + "tasks"); + if (openingTasks == null) { + openingTasks = new ArrayList<>(); } - openingTask = change; + openingTasks.add(change); } } } - if (openingTask == null) return false; - mOpeningLeash = openingTask.getLeash(); - if (openingTask.getContainer().equals(mPausingTask)) { - // In this case, we are "returning" to the already running app, so just consume + if (openingTasks == null) return false; + int pauseMatches = 0; + for (int i = 0; i < openingTasks.size(); ++i) { + if (mPausingTasks.contains(openingTasks.get(i).getContainer())) { + ++pauseMatches; + } + if (openingTasks.get(i).getContainer().equals(mPausingTasks.get(i))) { + // In this case, we are "returning" to an already running app, so just consume + // the merge and do nothing. + } + } + if (pauseMatches > 0) { + if (pauseMatches != mPausingTasks.size()) { + // We are not really "returning" properly... something went wrong. + throw new IllegalStateException("\"Concelling\" a recents transitions by " + + "unpausing " + pauseMatches + " apps after pausing " + + mPausingTasks.size() + " apps."); + } + // In this case, we are "returning" to an already running app, so just consume // the merge and do nothing. return true; } - // We are receiving a new opening task, so convert to onTaskAppeared. final int layer = mInfo.getChanges().size() * 3; - final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat( - openingTask, layer, mInfo, t); - mLeashMap.put(mOpeningLeash, target.leash.mSurfaceControl); - t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash()); - t.setLayer(target.leash.mSurfaceControl, layer); - t.hide(target.leash.mSurfaceControl); - t.apply(); - recents.onTaskAppeared(target); + mOpeningLeashes = new ArrayList<>(); + final RemoteAnimationTargetCompat[] targets = + new RemoteAnimationTargetCompat[openingTasks.size()]; + for (int i = 0; i < openingTasks.size(); ++i) { + mOpeningLeashes.add(openingTasks.get(i).getLeash()); + // We are receiving new opening tasks, so convert to onTasksAppeared. + final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat( + openingTasks.get(i), layer, mInfo, t); + mLeashMap.put(mOpeningLeashes.get(i), target.leash.mSurfaceControl); + t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash()); + t.setLayer(target.leash.mSurfaceControl, layer); + t.hide(target.leash.mSurfaceControl); + t.apply(); + targets[i] = target; + } + recents.onTasksAppeared(targets); return true; } @@ -292,21 +315,26 @@ public class RemoteTransitionCompat implements Parcelable { } if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint); try { - if (!toHome && mPausingTask != null && mOpeningLeash == null) { + if (!toHome && mPausingTasks != null && mOpeningLeashes == null) { // The gesture went back to opening the app rather than continuing with // recents, so end the transition by moving the app back to the top (and also // re-showing it's task). final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reorder(mPausingTask, true /* onTop */); final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - t.show(mInfo.getChange(mPausingTask).getLeash()); + for (int i = mPausingTasks.size() - 1; i >= 0; ++i) { + // reverse order so that index 0 ends up on top + wct.reorder(mPausingTasks.get(i), true /* onTop */); + t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash()); + } mFinishCB.onTransitionFinished(wct, t); } else { - if (mOpeningLeash != null) { + if (mOpeningLeashes != null) { // TODO: the launcher animation should handle this final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - t.show(mOpeningLeash); - t.setAlpha(mOpeningLeash, 1.f); + for (int i = 0; i < mOpeningLeashes.size(); ++i) { + t.show(mOpeningLeashes.get(i)); + t.setAlpha(mOpeningLeashes.get(i), 1.f); + } t.apply(); } if (mPipTask != null && mPipTransaction != null) { @@ -339,9 +367,9 @@ public class RemoteTransitionCompat implements Parcelable { // Reset all members. mWrapped = null; mFinishCB = null; - mPausingTask = null; + mPausingTasks = null; mInfo = null; - mOpeningLeash = null; + mOpeningLeashes = null; mLeashMap = null; mTransition = null; } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 535a061ee4ab..f94777339fae 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -934,6 +934,10 @@ public class AppTransitionController { voiceInteraction); applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp, voiceInteraction); + final RecentsAnimationController rac = mService.getRecentsAnimationController(); + if (rac != null) { + rac.sendTasksAppeared(); + } for (int i = 0; i < openingApps.size(); ++i) { openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 2057b1cdf24b..fd4b63e26403 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; @@ -163,6 +164,8 @@ public class RecentsAnimationController implements DeathRecipient { private boolean mNavigationBarAttachedToApp; private ActivityRecord mNavBarAttachedApp; + private final ArrayList mPendingTaskAppears = new ArrayList<>(); + /** * An app transition listener to cancel the recents animation only after the app transition * starts or is canceled. @@ -732,11 +735,19 @@ public class RecentsAnimationController implements DeathRecipient { return; } ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addTaskToTargets, target: %s", target); - try { - mRunner.onTaskAppeared(target); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to report task appeared", e); - } + mPendingTaskAppears.add(target); + } + } + + void sendTasksAppeared() { + if (mPendingTaskAppears.isEmpty() || mRunner == null) return; + try { + final RemoteAnimationTarget[] targets = mPendingTaskAppears.toArray( + new RemoteAnimationTarget[0]); + mRunner.onTasksAppeared(targets); + mPendingTaskAppears.clear(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report task appeared", e); } } @@ -744,10 +755,15 @@ public class RecentsAnimationController implements DeathRecipient { OnAnimationFinishedCallback finishedCallback) { final SparseBooleanArray recentTaskIds = mService.mAtmService.getRecentTasks().getRecentTaskIds(); + // The target must be built off the root task (the leaf task surface would be cropped + // within the root surface). However, recents only tracks leaf task ids, so we'll replace + // the task-id with the leaf id. + final Task leafTask = task.getTopLeafTask(); + int taskId = leafTask.mTaskId; TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task, - !recentTaskIds.get(task.mTaskId), true /* hidden */, finishedCallback); - mPendingNewTaskTargets.add(task.mTaskId); - return adapter.createRemoteAnimationTarget(); + !recentTaskIds.get(taskId), true /* hidden */, finishedCallback); + mPendingNewTaskTargets.add(taskId); + return adapter.createRemoteAnimationTarget(taskId); } void logRecentsAnimationStartTime(int durationMs) { @@ -782,7 +798,8 @@ public class RecentsAnimationController implements DeathRecipient { final ArrayList targets = new ArrayList<>(); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); - final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationTarget(); + final RemoteAnimationTarget target = + taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID); if (target != null) { targets.add(target); } else { @@ -995,6 +1012,8 @@ public class RecentsAnimationController implements DeathRecipient { removeAnimation(taskAdapter); taskAdapter.onCleanup(); } + // Should already be empty, but clean-up pending task-appears in-case they weren't sent. + mPendingTaskAppears.clear(); for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) { final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i); @@ -1224,7 +1243,14 @@ public class RecentsAnimationController implements DeathRecipient { mLocalBounds.offsetTo(tmpPos.x, tmpPos.y); } - RemoteAnimationTarget createRemoteAnimationTarget() { + /** + * @param overrideTaskId overrides the target's taskId. It may differ from mTaskId and thus + * can differ from taskInfo. This mismatch is needed, however, in + * some cases where we are animating root tasks but need need leaf + * ids for identification. If this is INVALID (-1), then mTaskId + * will be used. + */ + RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId) { final ActivityRecord topApp = mTask.getTopVisibleActivity(); final WindowState mainWindow = topApp != null ? topApp.findMainWindow() @@ -1238,7 +1264,10 @@ public class RecentsAnimationController implements DeathRecipient { final int mode = topApp.getActivityType() == mTargetActivityType ? MODE_OPENING : MODE_CLOSING; - mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash, + if (overrideTaskId < 0) { + overrideTaskId = mTask.mTaskId; + } + mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash, !topApp.fillsParent(), new Rect(), insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), mLocalBounds, mBounds, mTask.getWindowConfiguration(), diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 1c0981a6b8ad..babdb099a931 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2380,6 +2380,16 @@ class Task extends TaskFragment { return true; } + /** Return the top-most leaf-task under this one, or this task if it is a leaf. */ + public Task getTopLeafTask() { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final Task child = mChildren.get(i).asTask(); + if (child == null) continue; + return child.getTopLeafTask(); + } + return this; + } + int getDescendantTaskCount() { final int[] currentCount = {0}; final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },