diff options
| author | 2023-03-10 01:17:59 +0000 | |
|---|---|---|
| committer | 2023-10-31 07:30:26 +0000 | |
| commit | 4b08007b1098f53af7b480f1c5e4cdaf45ea8366 (patch) | |
| tree | d703b566d1e275e43c9a8ea41cee06add1c15f37 | |
| parent | 253e11fc1b40cd2643d7c0ceb4f35962eecd62e6 (diff) | |
Support task fragment for back navigation while adjacent.
Extends the opening targets to two activities.
For supports TaskFragment when adjacent, the opening target can be two
activities, here are the extra scenarios:
1. Task switch. The opening task can contains two activities, so when
using launchBehind to draw the activities, there will need to set flag
for them. And there should no extra work when using task snapshot.
2. Activity switch.
- If the close activity is adjacent to another one, and that one is the
previous activity, no predict animation because we don't know what will
happen.
- If the close activity is adjacent to another one, but that one isn't
the previous activity, start cross-activity animation as usual.
- If there is only one close activity, but the previous activity is
adjacent to another one, the opening target should be two activities.
TODO: for Activity switch case, the windowless starting window might be
two activity snapshot, or one splash screen to occlude two activities.
Bug: 274997067
Bug: 290845371
Test: atest BackNavigationControllerTests BackGestureInvokedTest
Test: invoke back navigation to the TaskFragment when adjacent.
Change-Id: I23e8f0156d1ba92d0a000c16c6c8469865885349
7 files changed, 475 insertions, 186 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java index 24479d7b5f39..a596cef9562a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java @@ -353,7 +353,6 @@ public class CrossActivityAnimation extends ShellBackAnimation { closingLeft += mapRange(interpolatedProgress, deltaXMin, deltaXMax); // Move the window along the Y axis. - final float deltaYRatio = (touchY - mInitialTouchPos.y) / height; final float closingTop = (height - closingHeight) * 0.5f; targetRect.set( closingLeft, closingTop, closingLeft + closingWidth, closingTop + closingHeight); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 9cd318f27355..723a4a7ca664 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -40,7 +40,6 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECI import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_RELAUNCH; -import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; @@ -422,11 +421,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { continue; } - // The back gesture has animated this change before transition happen, so here we don't - // play the animation again. - if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { - continue; - } // Don't animate anything that isn't independent. if (!TransitionInfo.isIndependent(change, info)) continue; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index baa9acaafa4b..c34a14e5b82b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_SLEEP; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.fixScale; +import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; @@ -743,6 +744,11 @@ public class Transitions implements RemoteCallable<Transitions>, if (!change.hasFlags(FLAG_IS_OCCLUDED)) { allOccluded = false; } + // The change has already animated by back gesture, don't need to play transition + // animation on it. + if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { + info.getChanges().remove(i); + } } // There does not need animation when: // A. Transfer starting window. Apply transfer starting window directly if there is no other diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 3c8e630dba2b..26f0d34a6261 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1585,7 +1585,7 @@ class ActivityClientController extends IActivityClientController.Stub { * the Activities in the Task should be finished when it finishes. Otherwise, return {@code * false}. */ - private boolean isRelativeTaskRootActivity(ActivityRecord r, ActivityRecord taskRoot) { + private static boolean isRelativeTaskRootActivity(ActivityRecord r, ActivityRecord taskRoot) { // Not a relative root if the given Activity is not the root Activity of its TaskFragment. final TaskFragment taskFragment = r.getTaskFragment(); if (r != taskFragment.getActivity(ar -> !ar.finishing || ar == r, @@ -1598,7 +1598,7 @@ class ActivityClientController extends IActivityClientController.Stub { return taskRoot.getTaskFragment().getCompanionTaskFragment() == taskFragment; } - private boolean isTopActivityInTaskFragment(ActivityRecord activity) { + private static boolean isTopActivityInTaskFragment(ActivityRecord activity) { return activity.getTaskFragment().topRunningActivity() == activity; } @@ -1614,9 +1614,6 @@ class ActivityClientController extends IActivityClientController.Stub { public void onBackPressed(IBinder token, IRequestFinishCallback callback) { final long origId = Binder.clearCallingIdentity(); try { - final Intent baseActivityIntent; - final boolean launchedFromHome; - final boolean isLastRunningActivity; synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); if (r == null) return; @@ -1624,39 +1621,16 @@ class ActivityClientController extends IActivityClientController.Stub { final Task task = r.getTask(); final ActivityRecord root = task.getRootActivity(false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/); - final boolean isTaskRoot = r == root; - if (isTaskRoot) { - if (mService.mWindowOrganizerController.mTaskOrganizerController + if (r == root && mService.mWindowOrganizerController.mTaskOrganizerController .handleInterceptBackPressedOnTaskRoot(r.getRootTask())) { - // This task is handled by a task organizer that has requested the back - // pressed callback. - return; - } - } else if (!isRelativeTaskRootActivity(r, root)) { - // Finish the Activity if the activity is not the task root or relative root. - requestCallbackFinish(callback); + // This task is handled by a task organizer that has requested the back + // pressed callback. + return; + } + if (shouldMoveTaskToBack(r, root)) { + moveActivityTaskToBack(token, true /* nonRoot */); return; } - - isLastRunningActivity = isTopActivityInTaskFragment(isTaskRoot ? root : r); - - final boolean isBaseActivity = root.mActivityComponent.equals(task.realActivity); - baseActivityIntent = isBaseActivity ? root.intent : null; - - launchedFromHome = root.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME); - } - - // If the activity was launched directly from the home screen, then we should - // refrain from finishing the activity and instead move it to the back to keep it in - // memory. The requirements for this are: - // 1. The activity is the last running activity in the task. - // 2. The current activity is the base activity for the task. - // 3. The activity was launched by the home process, and is one of the main entry - // points for the application. - if (baseActivityIntent != null && isLastRunningActivity - && launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent)) { - moveActivityTaskToBack(token, true /* nonRoot */); - return; } // The default option for handling the back button is to finish the Activity. @@ -1666,6 +1640,27 @@ class ActivityClientController extends IActivityClientController.Stub { } } + static boolean shouldMoveTaskToBack(ActivityRecord r, ActivityRecord rootActivity) { + if (r != rootActivity && !isRelativeTaskRootActivity(r, rootActivity)) { + return false; + } + final boolean isBaseActivity = rootActivity.mActivityComponent.equals( + r.getTask().realActivity); + final Intent baseActivityIntent = isBaseActivity ? rootActivity.intent : null; + + // If the activity was launched directly from the home screen, then we should + // refrain from finishing the activity and instead move it to the back to keep it in + // memory. The requirements for this are: + // 1. The activity is the last running activity in the task. + // 2. The current activity is the base activity for the task. + // 3. The activity was launched by the home process, and is one of the main entry + // points for the application. + return baseActivityIntent != null + && isTopActivityInTaskFragment(r) + && rootActivity.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME) + && ActivityRecord.isMainIntent(baseActivityIntent); + } + @Override public void enableTaskLocaleOverride(IBinder token) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d1728d6b55e0..2d37b9b2745d 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -43,6 +43,7 @@ import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -64,6 +65,7 @@ import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; /** @@ -141,10 +143,6 @@ class BackNavigationController { // multiple Activities in the Stack. Task prevTask = null; - // The previous activity we're going back to. This can be either a child of currentTask - // if there are more than one Activity in currentTask, or a child of prevTask, if - // currentActivity is the last child of currentTask. - ActivityRecord prevActivity; WindowContainer<?> removedWindowContainer = null; WindowState window; @@ -264,14 +262,21 @@ class BackNavigationController { return infoBuilder.build(); } + // The previous activity we're going back to. This can be either a child of currentTask + // if there are more than one Activity in currentTask, or a child of prevTask, if + // currentActivity is the last child of currentTask. // We don't have an application callback, let's find the destination of the back gesture // The search logic should align with ActivityClientController#finishActivity - prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); + final ArrayList<ActivityRecord> prevActivities = new ArrayList<>(); + final boolean canAnimate = getAnimatablePrevActivities(currentTask, currentActivity, + prevActivities); final boolean isOccluded = isKeyguardOccluded(window); - // TODO Dialog window does not need to attach on activity, check - // window.mAttrs.type != TYPE_BASE_APPLICATION - if ((window.getParent().getChildCount() > 1 + if (!canAnimate) { + backType = BackNavigationInfo.TYPE_CALLBACK; + } else if ((window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0) != window)) { + // TODO Dialog window does not need to attach on activity, check + // window.mAttrs.type != TYPE_BASE_APPLICATION // Are we the top window of our parent? If not, we are a window on top of the // activity, we won't close the activity. backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; @@ -279,9 +284,8 @@ class BackNavigationController { } else if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) { // skip if current activity is translucent backType = BackNavigationInfo.TYPE_CALLBACK; - removedWindowContainer = window; - } else if (prevActivity != null) { - if (!isOccluded || prevActivity.canShowWhenLocked()) { + } else if (prevActivities.size() > 0) { + if (!isOccluded || prevActivities.get(0).canShowWhenLocked()) { // We have another Activity in the same currentTask to go to final WindowContainer parent = currentActivity.getParent(); final boolean canCustomize = parent != null @@ -303,40 +307,45 @@ class BackNavigationController { } } removedWindowContainer = currentActivity; - prevTask = prevActivity.getTask(); + prevTask = prevActivities.get(0).getTask(); backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; } else { + // keyguard locked and activities are unable to show when locked. backType = BackNavigationInfo.TYPE_CALLBACK; } - } else if (currentTask.returnsToHomeRootTask()) { - if (isOccluded) { + } else { + // TODO(208789724): Create single source of truth for this, maybe in + // RootWindowContainer + prevTask = currentTask.mRootWindowContainer.getTask(t -> { + if (t.showToCurrentUser() && !t.mChildren.isEmpty()) { + final ActivityRecord ar = t.getTopNonFinishingActivity(); + return ar != null && ar.showToCurrentUser(); + } + return false; + }, currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/); + final ActivityRecord tmpPre = prevTask.getTopNonFinishingActivity(); + if (tmpPre != null) { + prevActivities.add(tmpPre); + findAdjacentActivityIfExist(tmpPre, prevActivities); + } + if (prevActivities.isEmpty() + || (isOccluded && !prevActivities.get(0).canShowWhenLocked())) { backType = BackNavigationInfo.TYPE_CALLBACK; - } else { - // Our Task should bring back to home + } else if (prevTask.isActivityTypeHome()) { removedWindowContainer = currentTask; - prevTask = currentTask.getDisplayArea().getRootHomeTask(); backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; mShowWallpaper = true; - } - } else if (currentActivity.isRootOfTask()) { - // TODO(208789724): Create single source of truth for this, maybe in - // RootWindowContainer - prevTask = currentTask.mRootWindowContainer.getTask(Task::showToCurrentUser, - currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/); - removedWindowContainer = currentTask; - // If it reaches the top activity, we will check the below task from parent. - // If it's null or multi-window, fallback the type to TYPE_CALLBACK. - // or set the type to proper value when it's return to home or another task. - if (prevTask == null || prevTask.inMultiWindowMode()) { - backType = BackNavigationInfo.TYPE_CALLBACK; } else { - prevActivity = prevTask.getTopNonFinishingActivity(); - if (prevActivity == null || (isOccluded && !prevActivity.canShowWhenLocked())) { + // If it reaches the top activity, we will check the below task from parent. + // If it's null or multi-window and has different parent task, fallback the type + // to TYPE_CALLBACK. Or set the type to proper value when it's return to home or + // another task. + final Task prevParent = prevTask.getParent().asTask(); + final Task currParent = currentTask.getParent().asTask(); + if (prevTask.inMultiWindowMode() && prevParent != currParent) { backType = BackNavigationInfo.TYPE_CALLBACK; - } else if (prevTask.isActivityTypeHome()) { - backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; - mShowWallpaper = true; } else { + removedWindowContainer = prevTask; backType = BackNavigationInfo.TYPE_CROSS_TASK; } } @@ -345,7 +354,8 @@ class BackNavigationController { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " + "removedContainer:%s, backType=%s", - prevActivity != null ? prevActivity.mActivityComponent : null, + prevActivities.size() > 0 ? TextUtils.join(";", prevActivities.stream() + .map(r -> r.mActivityComponent).toArray()) : null, prevTask != null ? prevTask.getName() : null, removedWindowContainer, BackNavigationInfo.typeToString(backType)); @@ -360,7 +370,7 @@ class BackNavigationController { if (prepareAnimation) { final AnimationHandler.ScheduleAnimationBuilder builder = mAnimationHandler.prepareAnimation(backType, adapter, - currentTask, prevTask, currentActivity, prevActivity); + currentTask, prevTask, currentActivity, prevActivities); mBackAnimationInProgress = builder != null; if (mBackAnimationInProgress) { if (removedWindowContainer.hasCommittedReparentToAnimationLeash() @@ -372,8 +382,8 @@ class BackNavigationController { // Current transition is still running, we have to defer the hiding to the // client process to prevent the unexpected relayout when handling the back // animation. - if (prevActivity != null) { - prevActivity.setDeferHidingClient(true); + for (int i = prevActivities.size() - 1; i >= 0; --i) { + prevActivities.get(i).setDeferHidingClient(true); } } else { scheduleAnimation(builder); @@ -381,17 +391,91 @@ class BackNavigationController { } } infoBuilder.setPrepareRemoteAnimation(prepareAnimation); - } // Release wm Lock - WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; - if (finalRemovedWindowContainer != null) { - final int finalBackType = backType; - RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( - result, finalBackType)); - infoBuilder.setOnBackNavigationDone(onBackNavigationDone); + if (removedWindowContainer != null) { + final int finalBackType = backType; + final RemoteCallback onBackNavigationDone = new RemoteCallback(result -> + onBackNavigationDone(result, finalBackType)); + infoBuilder.setOnBackNavigationDone(onBackNavigationDone); + } else { + mNavigationMonitor.stopMonitorForRemote(); + } + mLastBackType = backType; + return infoBuilder.build(); } - mLastBackType = backType; - return infoBuilder.build(); + } + + /** + * Gets previous activities from currentActivity. + * + * @return false if unable to predict what will happen + */ + private static boolean getAnimatablePrevActivities(@NonNull Task currentTask, + @NonNull ActivityRecord currentActivity, + @NonNull ArrayList<ActivityRecord> outPrevActivities) { + if (currentActivity.mAtmService + .mTaskOrganizerController.shouldInterceptBackPressedOnRootTask( + currentTask.getRootTask())) { + // The task organizer will handle back pressed, don't play animation. + return false; + } + final ActivityRecord root = currentTask.getRootActivity(false /*ignoreRelinquishIdentity*/, + true /*setToBottomIfNone*/); + if (root != null && ActivityClientController.shouldMoveTaskToBack(currentActivity, root)) { + return true; + } + + // Searching previous + final ActivityRecord prevActivity = currentTask.getActivity((below) -> !below.finishing, + currentActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/); + if (prevActivity == null) { + // No previous activity in this task, can still predict if previous task exists. + return true; + } + if (currentTask.getActivity((above) -> !above.finishing, currentActivity, + false /*includeBoundary*/, false /*traverseTopToBottom*/) != null) { + // another activity is above this activity, don't know what will happen + return false; + } + + final TaskFragment currTF = currentActivity.getTaskFragment(); + final TaskFragment prevTF = prevActivity.getTaskFragment(); + if (currTF != prevTF && prevTF != null) { + final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment(); + if (prevTFAdjacent != null) { + if (prevTFAdjacent == currTF) { + // Cannot predict what will happen when app receive back key, skip animation. + outPrevActivities.clear(); + return false; + } else { + final ActivityRecord prevActivityAdjacent = + prevTFAdjacent.getTopNonFinishingActivity(); + if (prevActivityAdjacent != null) { + outPrevActivities.add(prevActivityAdjacent); + } else { + // Don't know what will happen. + outPrevActivities.clear(); + return false; + } + } + } + } + outPrevActivities.add(prevActivity); + return true; + } + + private static void findAdjacentActivityIfExist(@NonNull ActivityRecord mainActivity, + @NonNull ArrayList<ActivityRecord> outList) { + final TaskFragment mainTF = mainActivity.getTaskFragment(); + if (mainTF == null || mainTF.getAdjacentTaskFragment() == null) { + return; + } + final TaskFragment adjacentTF = mainTF.getAdjacentTaskFragment(); + final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity(); + if (topActivity == null) { + return; + } + outList.add(topActivity); } boolean isMonitoringTransition() { @@ -696,7 +780,7 @@ class BackNavigationController { if (!hasTarget) { // Skip if no target participated in current finished transition. Slog.w(TAG, "Finished transition didn't include the targets" - + " open: " + mPendingAnimationBuilder.mOpenTarget + + " open: " + Arrays.toString(mPendingAnimationBuilder.mOpenTargets) + " close: " + mPendingAnimationBuilder.mCloseTarget); cancelPendingAnimation(); return false; @@ -732,7 +816,7 @@ class BackNavigationController { private final boolean mShowWindowlessSurface; private final WindowManagerService mWindowManagerService; private BackWindowAnimationAdaptor mCloseAdaptor; - private BackWindowAnimationAdaptor mOpenAdaptor; + private BackWindowAnimationAdaptorWrapper mOpenAnimAdaptor; private boolean mComposed; private boolean mWaitTransition; private int mSwitchType = UNKNOWN; @@ -741,7 +825,7 @@ class BackNavigationController { // exactly match animating target. When target match, reparent the starting surface to // the opening target like starting window do. private boolean mStartingSurfaceTargetMatch; - private ActivityRecord mOpenActivity; + private ActivityRecord[] mOpenActivities; AnimationHandler(WindowManagerService wms) { mWindowManagerService = wms; @@ -753,61 +837,69 @@ class BackNavigationController { private static final int TASK_SWITCH = 1; private static final int ACTIVITY_SWITCH = 2; - private static boolean isActivitySwitch(WindowContainer close, WindowContainer open) { - if (close.asActivityRecord() == null || open.asActivityRecord() == null - || (close.asActivityRecord().getTask() - != open.asActivityRecord().getTask())) { + private static boolean isActivitySwitch(@NonNull WindowContainer close, + @NonNull WindowContainer[] open) { + if (open == null || open.length == 0 || close.asActivityRecord() == null) { return false; } + final Task closeTask = close.asActivityRecord().getTask(); + for (int i = open.length - 1; i >= 0; --i) { + if (open[i].asActivityRecord() == null + || (closeTask != open[i].asActivityRecord().getTask())) { + return false; + } + } return true; } - private static boolean isTaskSwitch(WindowContainer close, WindowContainer open) { - if (close.asTask() == null || open.asTask() == null - || (close.asTask() == open.asTask())) { + private static boolean isTaskSwitch(@NonNull WindowContainer close, + @NonNull WindowContainer[] open) { + if (open == null || open.length != 1 || close.asTask() == null) { return false; } - return true; + return open[0].asTask() != null && (close.asTask() != open[0].asTask()); } - private void initiate(WindowContainer close, WindowContainer open, - ActivityRecord openActivity) { - WindowContainer closeTarget; + private void initiate(@NonNull WindowContainer close, @NonNull WindowContainer[] open, + @NonNull ActivityRecord[] openingActivities) { if (isActivitySwitch(close, open)) { mSwitchType = ACTIVITY_SWITCH; - closeTarget = close.asActivityRecord(); } else if (isTaskSwitch(close, open)) { mSwitchType = TASK_SWITCH; - // The transition target must be activity in legacy transition. - closeTarget = WindowManagerService.sEnableShellTransitions ? close - : close.asTask().getTopNonFinishingActivity(); } else { mSwitchType = UNKNOWN; return; } - mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType); - mOpenAdaptor = createAdaptor(open, true, mSwitchType); - mOpenActivity = openActivity; - if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) { + mCloseAdaptor = createAdaptor(close, false, mSwitchType); + if (mCloseAdaptor.mAnimationTarget == null) { Slog.w(TAG, "composeNewAnimations fail, skip"); clearBackAnimateTarget(); + return; } + + mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open); + if (!mOpenAnimAdaptor.isValid()) { + Slog.w(TAG, "compose animations fail, skip"); + clearBackAnimateTarget(); + return; + } + mOpenActivities = openingActivities; } private boolean composeAnimations(@NonNull WindowContainer close, - @NonNull WindowContainer open, ActivityRecord openActivity) { + @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities) { if (mComposed || mWaitTransition) { Slog.e(TAG, "Previous animation is running " + this); return false; } clearBackAnimateTarget(); - if (close == null || open == null || openActivity == null) { + if (close == null || open == null || open.length == 0 || open.length > 2) { Slog.e(TAG, "reset animation with null target close: " - + close + " open: " + open); + + close + " open: " + Arrays.toString(open)); return false; } - initiate(close, open, openActivity); + initiate(close, open, openingActivities); if (mSwitchType == UNKNOWN) { return false; } @@ -816,9 +908,14 @@ class BackNavigationController { return true; } - RemoteAnimationTarget[] getAnimationTargets() { - return mComposed ? new RemoteAnimationTarget[] { - mCloseAdaptor.mAnimationTarget, mOpenAdaptor.mAnimationTarget} : null; + @Nullable RemoteAnimationTarget[] getAnimationTargets() { + if (!mComposed) { + return null; + } + final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[2]; + targets[0] = mCloseAdaptor.mAnimationTarget; + targets[1] = mOpenAnimAdaptor.getOrCreateAnimationTarget(); + return targets; } boolean isSupportWindowlessSurface() { @@ -826,7 +923,7 @@ class BackNavigationController { .isSupportWindowlessStartingSurface(); } - boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) { + boolean containTarget(@NonNull ArrayList<WindowContainer> wcs, boolean open) { for (int i = wcs.size() - 1; i >= 0; --i) { if (isTarget(wcs.get(i), open)) { return true; @@ -835,22 +932,35 @@ class BackNavigationController { return wcs.isEmpty(); } - boolean isTarget(WindowContainer wc, boolean open) { + boolean isTarget(@NonNull WindowContainer wc, boolean open) { if (!mComposed) { return false; } + if (open) { + for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { + if (isAnimateTarget(wc, mOpenAnimAdaptor.mAdaptors[i].mTarget, mSwitchType)) { + return true; + } + } + return false; + } + return isAnimateTarget(wc, mCloseAdaptor.mTarget, mSwitchType); + } - // WC must be ActivityRecord in legacy transition, but it also can be Task or - // TaskFragment when using Shell transition. - // Open target: Can be Task or ActivityRecord or TaskFragment - // Close target: Limit to the top activity for now, to reduce the chance of misjudgment. - final WindowContainer target = open ? mOpenAdaptor.mTarget : mCloseAdaptor.mTarget; - if (mSwitchType == TASK_SWITCH) { - return wc == target - || (wc.asTask() != null && wc.hasChild(target)) - || (wc.asActivityRecord() != null && target.hasChild(wc)); - } else if (mSwitchType == ACTIVITY_SWITCH) { - return wc == target || (wc.asTaskFragment() != null && wc.hasChild(target)); + private static boolean isAnimateTarget(@NonNull WindowContainer window, + @NonNull WindowContainer animationTarget, int switchType) { + if (switchType == TASK_SWITCH) { + // simplify home search for multiple hierarchy + if (window.isActivityTypeHome() && animationTarget.isActivityTypeHome()) { + return true; + } + return window == animationTarget + || (animationTarget.asTask() != null && animationTarget.hasChild(window)) + || (animationTarget.asActivityRecord() != null + && window.hasChild(animationTarget)); + } else if (switchType == ACTIVITY_SWITCH) { + return window == animationTarget + || (window.asTaskFragment() != null && window.hasChild(animationTarget)); } return false; } @@ -864,19 +974,25 @@ class BackNavigationController { mCloseAdaptor.mTarget.cancelAnimation(); mCloseAdaptor = null; } - if (mOpenAdaptor != null) { - mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch); - mOpenAdaptor.mTarget.cancelAnimation(); - mOpenAdaptor = null; + if (mOpenAnimAdaptor != null) { + mOpenAnimAdaptor.cleanUp(mStartingSurfaceTargetMatch); + mOpenAnimAdaptor = null; } - if (mOpenActivity != null && mOpenActivity.mLaunchTaskBehind) { - restoreLaunchBehind(mOpenActivity); + + if (mOpenActivities != null) { + for (int i = mOpenActivities.length - 1; i >= 0; --i) { + if (mOpenActivities[i].mLaunchTaskBehind) { + restoreLaunchBehind(mOpenActivities[i]); + } + } } } void markStartingSurfaceMatch() { mStartingSurfaceTargetMatch = true; - mOpenAdaptor.reparentWindowlessSurfaceToTarget(); + for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { + mOpenAnimAdaptor.mAdaptors[i].reparentWindowlessSurfaceToTarget(); + } } void clearBackAnimateTarget() { @@ -885,13 +1001,13 @@ class BackNavigationController { mWaitTransition = false; mStartingSurfaceTargetMatch = false; mSwitchType = UNKNOWN; - mOpenActivity = null; + mOpenActivities = null; } // The close target must in close list // The open target can either in close or open list - boolean containsBackAnimationTargets(ArrayList<WindowContainer> openApps, - ArrayList<WindowContainer> closeApps) { + boolean containsBackAnimationTargets(@NonNull ArrayList<WindowContainer> openApps, + @NonNull ArrayList<WindowContainer> closeApps) { return containTarget(closeApps, false /* open */) && (containTarget(openApps, true /* open */) || containTarget(openApps, false /* open */)); @@ -901,9 +1017,9 @@ class BackNavigationController { public String toString() { return "AnimationTargets{" + " openTarget= " - + (mOpenAdaptor != null ? mOpenAdaptor.mTarget : "null") + + (mOpenAnimAdaptor != null ? dumpOpenAnimTargetsToString() : null) + " closeTarget= " - + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : "null") + + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : null) + " mSwitchType= " + mSwitchType + " mComposed= " @@ -913,8 +1029,21 @@ class BackNavigationController { + '}'; } - private static BackWindowAnimationAdaptor createAdaptor( - WindowContainer target, boolean isOpen, int switchType) { + private String dumpOpenAnimTargetsToString() { + final StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (int i = 0; i < mOpenAnimAdaptor.mAdaptors.length; i++) { + if (i > 0) { + sb.append(','); + } + sb.append(mOpenAnimAdaptor.mAdaptors[i].mTarget); + } + sb.append("}"); + return sb.toString(); + } + + @NonNull private static BackWindowAnimationAdaptor createAdaptor( + @NonNull WindowContainer target, boolean isOpen, int switchType) { final BackWindowAnimationAdaptor adaptor = new BackWindowAnimationAdaptor(target, isOpen, switchType); final SurfaceControl.Transaction pt = target.getPendingTransaction(); @@ -930,6 +1059,100 @@ class BackNavigationController { return adaptor; } + private static class BackWindowAnimationAdaptorWrapper { + final BackWindowAnimationAdaptor[] mAdaptors; + SurfaceControl.Transaction mCloseTransaction; + + BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType, + @NonNull WindowContainer... targets) { + mAdaptors = new BackWindowAnimationAdaptor[targets.length]; + for (int i = targets.length - 1; i >= 0; --i) { + mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType); + } + } + + boolean isValid() { + for (int i = mAdaptors.length - 1; i >= 0; --i) { + if (mAdaptors[i].mAnimationTarget == null) { + return false; + } + } + return true; + } + + void cleanUp(boolean startingSurfaceMatch) { + for (int i = mAdaptors.length - 1; i >= 0; --i) { + mAdaptors[i].cleanUpWindowlessSurface(startingSurfaceMatch); + mAdaptors[i].mTarget.cancelAnimation(); + } + if (mCloseTransaction != null) { + mCloseTransaction.apply(); + mCloseTransaction = null; + } + } + + void onAnimationFinish() { + final SurfaceControl.Transaction pt = mAdaptors[0].mTarget.getPendingTransaction(); + if (mCloseTransaction != null) { + pt.merge(mCloseTransaction); + mCloseTransaction = null; + } + if (mAdaptors.length > 1) { + for (int i = mAdaptors.length - 1; i >= 0; --i) { + final WindowContainer wc = mAdaptors[i].mTarget; + final WindowContainer parent = wc.getParent(); + if (parent != null) { + pt.reparent(wc.getSurfaceControl(), + parent.getSurfaceControl()); + } + } + } + } + + @NonNull RemoteAnimationTarget getOrCreateAnimationTarget() { + // Special handle for opening two activities together. + // If we animate both activities separately, the animation area and rounded corner + // would also being handled separately. To make them seem like "open" together, wrap + // their leash with another animation leash. + if (mAdaptors.length > 1 && mCloseTransaction == null) { + final Rect unionBounds = new Rect(); + for (int i = mAdaptors.length - 1; i >= 0; --i) { + unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds); + } + final WindowContainer wc = mAdaptors[0].mTarget; + final Task task = wc.asActivityRecord() != null + ? wc.asActivityRecord().getTask() : wc.asTask(); + final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget; + final SurfaceControl leashSurface = new SurfaceControl.Builder() + .setName("cross-animation-leash") + .setContainerLayer() + .setHidden(false) + .setParent(task.getSurfaceControl()) + .build(); + final SurfaceControl.Transaction pt = wc.getPendingTransaction(); + pt.setLayer(leashSurface, wc.getParent().getLastLayer()); + mCloseTransaction = new SurfaceControl.Transaction(); + mCloseTransaction.reparent(leashSurface, null); + for (int i = mAdaptors.length - 1; i >= 0; --i) { + BackWindowAnimationAdaptor adaptor = mAdaptors[i]; + pt.reparent(adaptor.mAnimationTarget.leash, leashSurface); + pt.setPosition(adaptor.mAnimationTarget.leash, + adaptor.mAnimationTarget.localBounds.left, + adaptor.mAnimationTarget.localBounds.top); + } + return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface, + represent.isTranslucent, represent.clipRect, represent.contentInsets, + represent.prefixOrderIndex, + new Point(unionBounds.left, unionBounds.top), + unionBounds, unionBounds, represent.windowConfiguration, + true /* isNotInRecents */, null, null, represent.taskInfo, + represent.allowEnterPip); + } else { + return mAdaptors[0].mAnimationTarget; + } + } + } + private static class BackWindowAnimationAdaptor implements AnimationAdapter { SurfaceControl mCapturedLeash; private final Rect mBounds = new Rect(); @@ -943,7 +1166,7 @@ class BackNavigationController { private int mRequestedStartingSurfaceId = INVALID_TASK_ID; private SurfaceControl mStartingSurface; - BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen, + BackWindowAnimationAdaptor(@NonNull WindowContainer target, boolean isOpen, int switchType) { mBounds.set(target.getBounds()); mTarget = target; @@ -1034,7 +1257,7 @@ class BackNavigationController { return mAnimationTarget; } - void createStartingSurface() { + void createStartingSurface(@NonNull WindowContainer closeWindow) { if (!mIsOpen) { return; } @@ -1053,7 +1276,10 @@ class BackNavigationController { final TaskSnapshot snapshot = getSnapshot(mTarget); mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController .addWindowlessStartingSurface(openTask, mainActivity, - mAnimationTarget.leash, snapshot, + // Choose configuration from closeWindow, because the configuration + // of opening target may not update before resume, so the starting + // surface should occlude it entirely. + mAnimationTarget.leash, snapshot, closeWindow.getConfiguration(), new IWindowlessStartingSurfaceCallback.Stub() { // Once the starting surface has been created in shell, it will call // onSurfaceAdded to pass the created surface to core, so if a @@ -1108,15 +1334,17 @@ class BackNavigationController { ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter, Task currentTask, Task previousTask, ActivityRecord currentActivity, - ActivityRecord previousActivity) { + ArrayList<ActivityRecord> previousActivity) { switch (backType) { case BackNavigationInfo.TYPE_RETURN_TO_HOME: return new ScheduleAnimationBuilder(backType, adapter) .setIsLaunchBehind(true) .setComposeTarget(currentTask, previousTask); case BackNavigationInfo.TYPE_CROSS_ACTIVITY: + ActivityRecord[] prevActs = new ActivityRecord[previousActivity.size()]; + prevActs = previousActivity.toArray(prevActs); return new ScheduleAnimationBuilder(backType, adapter) - .setComposeTarget(currentActivity, previousActivity) + .setComposeTarget(currentActivity, prevActs) .setIsLaunchBehind(false); case BackNavigationInfo.TYPE_CROSS_TASK: return new ScheduleAnimationBuilder(backType, adapter) @@ -1130,7 +1358,7 @@ class BackNavigationController { final int mType; final BackAnimationAdapter mBackAnimationAdapter; WindowContainer mCloseTarget; - WindowContainer mOpenTarget; + WindowContainer[] mOpenTargets; boolean mIsLaunchBehind; ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) { @@ -1138,9 +1366,10 @@ class BackNavigationController { mBackAnimationAdapter = backAnimationAdapter; } - ScheduleAnimationBuilder setComposeTarget(WindowContainer close, WindowContainer open) { + ScheduleAnimationBuilder setComposeTarget(@NonNull WindowContainer close, + @NonNull WindowContainer... open) { mCloseTarget = close; - mOpenTarget = open; + mOpenTargets = open; return this; } @@ -1150,43 +1379,60 @@ class BackNavigationController { } boolean containTarget(@NonNull WindowContainer wc) { - return wc == mOpenTarget || wc == mCloseTarget - || mOpenTarget.hasChild(wc) || mCloseTarget.hasChild(wc); + if (mOpenTargets != null) { + for (int i = mOpenTargets.length - 1; i >= 0; --i) { + if (wc == mOpenTargets[i] || mOpenTargets[i].hasChild(wc)) { + return true; + } + } + } + return wc == mCloseTarget || mCloseTarget.hasChild(wc); } /** * Apply preview strategy on the opening target + * @param closeWindow The close window, where it's configuration should cover all + * open target(s). * @param openAnimationAdaptor The animator who can create starting surface. - * @param visibleOpenActivity The visible activity in opening target. + * @param visibleOpenActivities The visible activities in opening targets. */ - private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor, - ActivityRecord visibleOpenActivity) { - if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) { - openAnimationAdaptor.createStartingSurface(); - return; + private void applyPreviewStrategy(@NonNull WindowContainer closeWindow, + @NonNull BackWindowAnimationAdaptor[] openAnimationAdaptor, + @NonNull ActivityRecord[] visibleOpenActivities) { + if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind + // TODO (b/274997067) Draw two snapshot in a single starting surface. + // We are using TaskId as the key of + // StartingSurfaceDrawer#StartingWindowRecordManager, so we cannot create + // two activity snapshot with WindowlessStartingWindow. + // Try to draw two snapshot within a WindowlessStartingWindow, or find + // another key for StartingWindowRecordManager. + && openAnimationAdaptor.length == 1) { + openAnimationAdaptor[0].createStartingSurface(closeWindow); + } else { + for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { + setLaunchBehind(visibleOpenActivities[i]); + } } - setLaunchBehind(visibleOpenActivity); } - Runnable build() { - if (mOpenTarget == null || mCloseTarget == null) { + @Nullable Runnable build() { + if (mOpenTargets == null || mCloseTarget == null || mOpenTargets.length == 0) { return null; } - final ActivityRecord openActivity = mOpenTarget.asTask() != null - ? mOpenTarget.asTask().getTopNonFinishingActivity() - : mOpenTarget.asActivityRecord() != null - ? mOpenTarget.asActivityRecord() : null; - if (openActivity == null) { + final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface(); + final ActivityRecord[] openingActivities = getTopOpenActivities(mOpenTargets); + + if (shouldLaunchBehind && openingActivities == null) { Slog.e(TAG, "No opening activity"); return null; } - if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) { + if (!composeAnimations(mCloseTarget, mOpenTargets, openingActivities)) { return null; } mCloseTarget.mTransitionController.mSnapshotController .mActivitySnapshotController.clearOnBackPressedActivities(); - applyPreviewStrategy(mOpenAdaptor, openActivity); + applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor.mAdaptors, openingActivities); final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(); final RemoteAnimationTarget[] targets = getAnimationTargets(); @@ -1210,6 +1456,7 @@ class BackNavigationController { // animation was canceled return; } + mOpenAnimAdaptor.onAnimationFinish(); if (!triggerBack) { clearBackAnimateTarget(); } else { @@ -1223,6 +1470,41 @@ class BackNavigationController { } } + /** + * Finds next opening activity(ies) based on open targets, which could be: + * 1. If the open window is Task, then the open activity can either be an activity, or + * two activities inside two TaskFragments + * 2. If the open window is Activity, then the open window can be an activity, or two + * adjacent TaskFragments below it. + */ + @Nullable + private static ActivityRecord[] getTopOpenActivities( + @NonNull WindowContainer[] openWindows) { + ActivityRecord[] openActivities = null; + final WindowContainer mainTarget = openWindows[0]; + if (mainTarget.asTask() != null) { + final ArrayList<ActivityRecord> inTaskActivities = new ArrayList<>(); + final Task task = mainTarget.asTask(); + final ActivityRecord tmpPreActivity = task.getTopNonFinishingActivity(); + if (tmpPreActivity != null) { + inTaskActivities.add(tmpPreActivity); + findAdjacentActivityIfExist(tmpPreActivity, inTaskActivities); + } + + openActivities = new ActivityRecord[inTaskActivities.size()]; + for (int i = inTaskActivities.size() - 1; i >= 0; --i) { + openActivities[i] = inTaskActivities.get(i); + } + } else if (mainTarget.asActivityRecord() != null) { + final int size = openWindows.length; + openActivities = new ActivityRecord[size]; + for (int i = size - 1; i >= 0; --i) { + openActivities[i] = openWindows[i].asActivityRecord(); + } + } + return openActivities; + } + private static void setLaunchBehind(@NonNull ActivityRecord activity) { if (!activity.isVisibleRequested()) { activity.setVisibility(true); @@ -1311,7 +1593,7 @@ class BackNavigationController { static TaskSnapshot getSnapshot(@NonNull WindowContainer w) { if (w.asTask() != null) { final Task task = w.asTask(); - return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( + return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( task.mTaskId, task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); } @@ -1340,8 +1622,10 @@ class BackNavigationController { proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress); proto.write(LAST_BACK_TYPE, mLastBackType); proto.write(SHOW_WALLPAPER, mShowWallpaper); - if (mAnimationHandler.mOpenActivity != null) { - mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY); + if (mAnimationHandler.mOpenAnimAdaptor != null + && mAnimationHandler.mOpenAnimAdaptor.mAdaptors.length > 0) { + mAnimationHandler.mOpenActivities[0].writeNameToProto( + proto, MAIN_OPEN_ACTIVITY); } else { proto.write(MAIN_OPEN_ACTIVITY, ""); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 41e49b9f0466..12392a63a467 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -35,6 +35,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; @@ -733,7 +734,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { * the task was removed from hierarchy. */ int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root, - TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) { + TaskSnapshot taskSnapshot, Configuration configuration, + IWindowlessStartingSurfaceCallback callback) { final Task rootTask = task.getRootTask(); if (rootTask == null) { return INVALID_TASK_ID; @@ -743,6 +745,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return INVALID_TASK_ID; } final StartingWindowInfo info = task.getStartingWindowInfo(activity); + info.taskInfo.configuration.setTo(configuration); info.taskInfo.taskDescription = activity.taskDescription; info.taskSnapshot = taskSnapshot; info.windowlessStartingSurfaceCallback = callback; @@ -1195,8 +1198,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } public boolean handleInterceptBackPressedOnTaskRoot(Task task) { - if (task == null || !task.isOrganized() - || !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) { + if (!shouldInterceptBackPressedOnRootTask(task)) { return false; } final TaskOrganizerPendingEventsQueue pendingEventsQueue = @@ -1229,6 +1231,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return true; } + boolean shouldInterceptBackPressedOnRootTask(Task task) { + return task != null && task.isOrganized() + && mInterceptBackPressedOnRootTasks.contains(task.mTaskId); + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.print(prefix); pw.println("TaskOrganizerController:"); diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index dd7dec0bdb2b..7b1fa036a016 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -76,6 +76,7 @@ import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; +import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -601,30 +602,33 @@ public class BackNavigationControllerTests extends WindowTestsBase { final Task task = createTask(mDefaultDisplay); final ActivityRecord bottomActivity = createActivityRecord(task); final ActivityRecord homeActivity = mRootHomeTask.getTopNonFinishingActivity(); - + final ArrayList<ActivityRecord> openActivities = new ArrayList<>(); + openActivities.add(homeActivity); final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toHomeBuilder = animationHandler.prepareAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME, - mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity); + mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, openActivities); assertTrue(toHomeBuilder.mIsLaunchBehind); toHomeBuilder.build(); - verify(mAtm.mTaskOrganizerController, never()) - .addWindowlessStartingSurface(any(), any(), any(), any(), any()); + verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface( + any(), any(), any(), any(), any(), any()); animationHandler.clearBackAnimateTarget(); + openActivities.clear(); // Back to ACTIVITY and TASK have the same logic, just with different target. final ActivityRecord topActivity = createActivityRecord(task); + openActivities.add(bottomActivity); final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toActivityBuilder = animationHandler.prepareAnimation( BackNavigationInfo.TYPE_CROSS_ACTIVITY, mBackAnimationAdapter, task, task, - topActivity, bottomActivity); + topActivity, openActivities); assertFalse(toActivityBuilder.mIsLaunchBehind); toActivityBuilder.build(); if (preferWindowlessSurface) { - verify(mAtm.mTaskOrganizerController) - .addWindowlessStartingSurface(any(), any(), any(), any(), any()); + verify(mAtm.mTaskOrganizerController).addWindowlessStartingSurface( + any(), any(), any(), any(), any(), any()); } else { - verify(mAtm.mTaskOrganizerController, never()) - .addWindowlessStartingSurface(any(), any(), any(), any(), any()); + verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface( + any(), any(), any(), any(), any(), any()); } } |