Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c1eff37d authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Android (Google) Code Review
Browse files

Merge "Support to close two TaskFragments in predict back animation" into main

parents b5fca840 b3c1f2da
Loading
Loading
Loading
Loading
+72 −30
Original line number Diff line number Diff line
@@ -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,44 +414,85 @@ 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.

        final TaskFragment currTF = currentActivity.getTaskFragment();
        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 (currentTask.getActivity((above) -> !above.finishing, currentActivity,
                false /*includeBoundary*/, false /*traverseTopToBottom*/) != null) {
            // another activity is above this activity, don't know what will happen
            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;
                }
                // 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;
            }
        }

        final TaskFragment currTF = currentActivity.getTaskFragment();
        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 (currTF != prevTF && prevTF != null) {
        if (prevTF == null || prevTF.asTask() != null) {
            return;
        }

        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,
                            false /*includeBoundary*/, true /*traverseTopToBottom*/) == null;
                } else {
        if (prevTFAdjacent == null || prevTFAdjacent.asTask() != null) {
            return;
        }
        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) {
+104 −0
Original line number Diff line number Diff line
@@ -241,6 +241,110 @@ public class BackNavigationControllerTests extends WindowTestsBase {
                .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
    }

    @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();