diff options
author | 2023-12-06 10:14:30 +0000 | |
---|---|---|
committer | 2023-12-06 10:14:30 +0000 | |
commit | c1eff37d21f21096cb0f2ea53c55474d53f4308a (patch) | |
tree | fda22413614f8f3d73a0a31ef2ebb435a5feb905 | |
parent | b5fca8403047015923807cf94b9049002d1930f2 (diff) | |
parent | b3c1f2da8005249c5c3e91c49fd0bce01be1b447 (diff) |
Merge "Support to close two TaskFragments in predict back animation" into main
-rw-r--r-- | services/core/java/com/android/server/wm/BackNavigationController.java | 102 | ||||
-rw-r--r-- | services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java | 104 |
2 files changed, 176 insertions, 30 deletions
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index be7b8559e39e..c5902c965b43 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -395,7 +395,8 @@ class BackNavigationController { * * @return false if unable to predict what will happen */ - private static boolean getAnimatablePrevActivities(@NonNull Task currentTask, + @VisibleForTesting + static boolean getAnimatablePrevActivities(@NonNull Task currentTask, @NonNull ActivityRecord currentActivity, @NonNull ArrayList<ActivityRecord> outPrevActivities) { if (currentActivity.mAtmService @@ -413,45 +414,86 @@ class BackNavigationController { // 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) { - outPrevActivities.clear(); - // No more activity in task, so it can predict if previous task exists. - // Otherwise, unable to predict what will happen when app receive - // back key, skip animation. - return currentTask.getActivity((below) -> !below.finishing, prevActivity, + if (currTF != null && currTF.asTask() == null) { + // The currentActivity is embedded, search for the candidate previous activities. + if (prevActivity != null && currTF.hasChild(prevActivity)) { + // PrevActivity is under the same task fragment, that's it. + outPrevActivities.add(prevActivity); + return true; + } + if (currTF.getAdjacentTaskFragment() != null) { + // The two TFs are adjacent (visually displayed side-by-side), search if any + // activity below the lowest one + // If companion, those two TF will be closed together. + if (currTF.getCompanionTaskFragment() != null) { + final WindowContainer commonParent = currTF.getParent(); + final TaskFragment adjacentTF = currTF.getAdjacentTaskFragment(); + final TaskFragment lowerTF = commonParent.mChildren.indexOf(currTF) + < commonParent.mChildren.indexOf(adjacentTF) + ? currTF : adjacentTF; + final ActivityRecord lowerActivity = lowerTF.getTopNonFinishingActivity(); + // TODO (b/274997067) close currTF + companionTF, open next activities if any. + // Allow to predict next task if no more activity in task. Or return previous + // activities for cross-activity animation. + return currentTask.getActivity((below) -> !below.finishing, lowerActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/) == null; - } else { - final ActivityRecord prevActivityAdjacent = - prevTFAdjacent.getTopNonFinishingActivity(); - if (prevActivityAdjacent != null) { - outPrevActivities.add(prevActivityAdjacent); - } else { - // Don't know what will happen. - outPrevActivities.clear(); - return false; - } } + // Unable to predict if no companion, it can only close current activity and make + // prev Activity full screened. + return false; + } else if (currTF.getCompanionTaskFragment() != null) { + // TF is isStacked, search bottom activity from companion TF. + // + // Sample hierarchy: search for underPrevious if any. + // Current TF + // Companion TF (bottomActivityInCompanion) + // Bottom Activity not inside companion TF (underPrevious) + final TaskFragment companionTF = currTF.getCompanionTaskFragment(); + // find bottom activity in Companion TF. + final ActivityRecord bottomActivityInCompanion = companionTF.getActivity( + (below) -> !below.finishing, false /* traverseTopToBottom */); + final ActivityRecord underPrevious = currentTask.getActivity( + (below) -> !below.finishing, bottomActivityInCompanion, + false /*includeBoundary*/, true /*traverseTopToBottom*/); + if (underPrevious != null) { + outPrevActivities.add(underPrevious); + addPreviousAdjacentActivityIfExist(underPrevious, outPrevActivities); + } + return true; } } + + if (prevActivity == null) { + // No previous activity in this Task nor TaskFragment, it can still predict if previous + // task exists. + return true; + } + // Add possible adjacent activity if prevActivity is embedded + addPreviousAdjacentActivityIfExist(prevActivity, outPrevActivities); outPrevActivities.add(prevActivity); return true; } + private static void addPreviousAdjacentActivityIfExist(@NonNull ActivityRecord prevActivity, + @NonNull ArrayList<ActivityRecord> outPrevActivities) { + final TaskFragment prevTF = prevActivity.getTaskFragment(); + if (prevTF == null || prevTF.asTask() != null) { + return; + } + + final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment(); + if (prevTFAdjacent == null || prevTFAdjacent.asTask() != null) { + return; + } + final ActivityRecord prevActivityAdjacent = + prevTFAdjacent.getTopNonFinishingActivity(); + if (prevActivityAdjacent != null) { + outPrevActivities.add(prevActivityAdjacent); + } + } + private static void findAdjacentActivityIfExist(@NonNull ActivityRecord mainActivity, @NonNull ArrayList<ActivityRecord> outList) { final TaskFragment mainTF = mainActivity.getTaskFragment(); 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 afea8114d508..4d4d397585b7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -242,6 +242,110 @@ public class BackNavigationControllerTests extends WindowTestsBase { } @Test + public void backTypeCrossActivityInTaskFragment() { + final Task task = createTask(mDefaultDisplay); + final TaskFragment tf1 = createTaskFragmentWithActivity(task); + final TaskFragment tf2 = createTaskFragmentWithActivity(task); + final ArrayList<ActivityRecord> outPrevActivities = new ArrayList<>(); + + ActivityRecord prevAr = tf1.getTopMostActivity(); + ActivityRecord topAr = tf2.getTopMostActivity(); + boolean predictable; + + // Stacked + no Companion => predict for previous activity. + // TF2 + // TF1 + predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, + outPrevActivities); + assertTrue(outPrevActivities.contains(prevAr)); + assertTrue(predictable); + outPrevActivities.clear(); + + // Stacked + companion => predict for previous task + tf2.setCompanionTaskFragment(tf1); + predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, + outPrevActivities); + assertTrue(outPrevActivities.isEmpty()); + assertTrue(predictable); + tf2.setCompanionTaskFragment(null); + + // Adjacent + no companion => unable to predict + // TF1 | TF2 + tf1.setAdjacentTaskFragment(tf2); + tf2.setAdjacentTaskFragment(tf1); + predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, + outPrevActivities); + assertTrue(outPrevActivities.isEmpty()); + assertFalse(predictable); + predictable = BackNavigationController.getAnimatablePrevActivities(task, prevAr, + outPrevActivities); + assertTrue(outPrevActivities.isEmpty()); + assertFalse(predictable); + + // Adjacent + companion => predict for previous task + tf1.setCompanionTaskFragment(tf2); + tf2.setCompanionTaskFragment(tf1); + predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, + outPrevActivities); + assertTrue(outPrevActivities.isEmpty()); + assertTrue(predictable); + predictable = BackNavigationController.getAnimatablePrevActivities(task, prevAr, + outPrevActivities); + assertTrue(outPrevActivities.isEmpty()); + assertTrue(predictable); + // reset + tf1.setAdjacentTaskFragment(null); + tf2.setAdjacentTaskFragment(null); + tf1.setCompanionTaskFragment(null); + tf2.setCompanionTaskFragment(null); + + final TaskFragment tf3 = new TaskFragmentBuilder(mAtm) + .createActivityCount(2) + .setParentTask(task) + .build(); + topAr = tf3.getTopMostActivity(); + prevAr = tf3.getBottomMostActivity(); + // Stacked => predict for previous activity. + // TF3 + // TF2 + // TF1 + predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, + outPrevActivities); + assertTrue(outPrevActivities.contains(prevAr)); + assertTrue(predictable); + // reset + outPrevActivities.clear(); + + // Adjacent => predict for previous activity. + // TF2 | TF3 + // TF1 + tf2.setAdjacentTaskFragment(tf3); + tf3.setAdjacentTaskFragment(tf2); + predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, + outPrevActivities); + assertTrue(outPrevActivities.contains(prevAr)); + assertTrue(predictable); + // reset + outPrevActivities.clear(); + tf2.setAdjacentTaskFragment(null); + tf3.setAdjacentTaskFragment(null); + + final TaskFragment tf4 = createTaskFragmentWithActivity(task); + // Stacked + companion => predict for previous activity below companion. + // Tf4 + // TF3 + // TF2 + // TF1 + tf4.setCompanionTaskFragment(tf3); + tf3.setCompanionTaskFragment(tf4); + topAr = tf4.getTopMostActivity(); + predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, + outPrevActivities); + assertTrue(outPrevActivities.contains(tf2.getTopMostActivity())); + assertTrue(predictable); + } + + @Test public void backTypeDialogCloseWhenBackFromDialog() { DialogCloseTestCase testCase = createTopTaskWithActivityAndDialog(); IOnBackInvokedCallback callback = withSystemCallback(testCase.task); |