Loading services/core/java/com/android/server/wm/BackNavigationController.java +45 −26 Original line number Diff line number Diff line Loading @@ -422,7 +422,6 @@ class BackNavigationController { // Searching previous final ActivityRecord prevActivity = currentTask.getActivity((below) -> !below.finishing, currentActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/); final TaskFragment currTF = currentActivity.getTaskFragment(); if (currTF != null && currTF.asTask() == null) { // The currentActivity is embedded, search for the candidate previous activities. Loading @@ -431,36 +430,17 @@ class BackNavigationController { 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; } // 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) { if (currTF.getAdjacentTaskFragment() == null) { final TaskFragment nextTF = findNextTaskFragment(currentTask, currTF); if (isSecondCompanionToFirst(currTF, nextTF)) { // 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( final ActivityRecord bottomActivityInCompanion = nextTF.getActivity( (below) -> !below.finishing, false /* traverseTopToBottom */); final ActivityRecord underPrevious = currentTask.getActivity( (below) -> !below.finishing, bottomActivityInCompanion, Loading @@ -471,6 +451,27 @@ class BackNavigationController { } return true; } } else { // If adjacent TF has companion to current TF, those two TF will be closed together. final TaskFragment adjacentTF = currTF.getAdjacentTaskFragment(); if (isSecondCompanionToFirst(currTF, adjacentTF)) { // The two TFs are adjacent (visually displayed side-by-side), search if any // activity below the lowest one. final WindowContainer commonParent = currTF.getParent(); 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; } } if (prevActivity == null) { Loading @@ -484,6 +485,24 @@ class BackNavigationController { return true; } private static TaskFragment findNextTaskFragment(@NonNull Task currentTask, @NonNull TaskFragment topTF) { final int topIndex = currentTask.mChildren.indexOf(topTF); if (topIndex <= 0) { return null; } final WindowContainer next = currentTask.mChildren.get(topIndex - 1); return next.asTaskFragment(); } /** * Whether the second TF has set companion to first TF. * When set, the second TF will be removed by organizer if the first TF is removed. */ private static boolean isSecondCompanionToFirst(TaskFragment first, TaskFragment second) { return second != null && second.getCompanionTaskFragment() == first; } private static void addPreviousAdjacentActivityIfExist(@NonNull ActivityRecord prevActivity, @NonNull ArrayList<ActivityRecord> outPrevActivities) { final TaskFragment prevTF = prevActivity.getTaskFragment(); Loading services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +25 −5 Original line number Diff line number Diff line Loading @@ -291,13 +291,22 @@ public class BackNavigationControllerTests extends WindowTestsBase { assertTrue(predictable); outPrevActivities.clear(); // Stacked + companion => predict for previous task // Stacked + top companion to bottom but bottom didn't => predict for previous activity tf2.setCompanionTaskFragment(tf1); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); assertTrue(outPrevActivities.contains(prevAr)); assertTrue(predictable); tf2.setCompanionTaskFragment(null); outPrevActivities.clear(); // Stacked + next companion to top => predict for previous task tf1.setCompanionTaskFragment(tf2); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); assertTrue(predictable); tf1.setCompanionTaskFragment(null); // Adjacent + no companion => unable to predict // TF1 | TF2 Loading @@ -314,11 +323,13 @@ public class BackNavigationControllerTests extends WindowTestsBase { // Adjacent + companion => predict for previous task tf1.setCompanionTaskFragment(tf2); tf2.setCompanionTaskFragment(tf1); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); assertTrue(predictable); tf1.setCompanionTaskFragment(null); tf2.setCompanionTaskFragment(tf1); predictable = BackNavigationController.getAnimatablePrevActivities(task, prevAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); Loading Loading @@ -361,18 +372,27 @@ public class BackNavigationControllerTests extends WindowTestsBase { tf3.setAdjacentTaskFragment(null); final TaskFragment tf4 = createTaskFragmentWithActivity(task); // Stacked + companion => predict for previous activity below companion. // Stacked + next companion to top => 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); outPrevActivities.clear(); tf3.setCompanionTaskFragment(null); // Stacked + top companion to next but next one didn't => predict for previous activity. tf4.setCompanionTaskFragment(tf3); topAr = tf4.getTopMostActivity(); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.contains(tf3.getTopMostActivity())); assertTrue(predictable); } @Test Loading Loading
services/core/java/com/android/server/wm/BackNavigationController.java +45 −26 Original line number Diff line number Diff line Loading @@ -422,7 +422,6 @@ class BackNavigationController { // Searching previous final ActivityRecord prevActivity = currentTask.getActivity((below) -> !below.finishing, currentActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/); final TaskFragment currTF = currentActivity.getTaskFragment(); if (currTF != null && currTF.asTask() == null) { // The currentActivity is embedded, search for the candidate previous activities. Loading @@ -431,36 +430,17 @@ class BackNavigationController { 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; } // 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) { if (currTF.getAdjacentTaskFragment() == null) { final TaskFragment nextTF = findNextTaskFragment(currentTask, currTF); if (isSecondCompanionToFirst(currTF, nextTF)) { // 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( final ActivityRecord bottomActivityInCompanion = nextTF.getActivity( (below) -> !below.finishing, false /* traverseTopToBottom */); final ActivityRecord underPrevious = currentTask.getActivity( (below) -> !below.finishing, bottomActivityInCompanion, Loading @@ -471,6 +451,27 @@ class BackNavigationController { } return true; } } else { // If adjacent TF has companion to current TF, those two TF will be closed together. final TaskFragment adjacentTF = currTF.getAdjacentTaskFragment(); if (isSecondCompanionToFirst(currTF, adjacentTF)) { // The two TFs are adjacent (visually displayed side-by-side), search if any // activity below the lowest one. final WindowContainer commonParent = currTF.getParent(); 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; } } if (prevActivity == null) { Loading @@ -484,6 +485,24 @@ class BackNavigationController { return true; } private static TaskFragment findNextTaskFragment(@NonNull Task currentTask, @NonNull TaskFragment topTF) { final int topIndex = currentTask.mChildren.indexOf(topTF); if (topIndex <= 0) { return null; } final WindowContainer next = currentTask.mChildren.get(topIndex - 1); return next.asTaskFragment(); } /** * Whether the second TF has set companion to first TF. * When set, the second TF will be removed by organizer if the first TF is removed. */ private static boolean isSecondCompanionToFirst(TaskFragment first, TaskFragment second) { return second != null && second.getCompanionTaskFragment() == first; } private static void addPreviousAdjacentActivityIfExist(@NonNull ActivityRecord prevActivity, @NonNull ArrayList<ActivityRecord> outPrevActivities) { final TaskFragment prevTF = prevActivity.getTaskFragment(); Loading
services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +25 −5 Original line number Diff line number Diff line Loading @@ -291,13 +291,22 @@ public class BackNavigationControllerTests extends WindowTestsBase { assertTrue(predictable); outPrevActivities.clear(); // Stacked + companion => predict for previous task // Stacked + top companion to bottom but bottom didn't => predict for previous activity tf2.setCompanionTaskFragment(tf1); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); assertTrue(outPrevActivities.contains(prevAr)); assertTrue(predictable); tf2.setCompanionTaskFragment(null); outPrevActivities.clear(); // Stacked + next companion to top => predict for previous task tf1.setCompanionTaskFragment(tf2); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); assertTrue(predictable); tf1.setCompanionTaskFragment(null); // Adjacent + no companion => unable to predict // TF1 | TF2 Loading @@ -314,11 +323,13 @@ public class BackNavigationControllerTests extends WindowTestsBase { // Adjacent + companion => predict for previous task tf1.setCompanionTaskFragment(tf2); tf2.setCompanionTaskFragment(tf1); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); assertTrue(predictable); tf1.setCompanionTaskFragment(null); tf2.setCompanionTaskFragment(tf1); predictable = BackNavigationController.getAnimatablePrevActivities(task, prevAr, outPrevActivities); assertTrue(outPrevActivities.isEmpty()); Loading Loading @@ -361,18 +372,27 @@ public class BackNavigationControllerTests extends WindowTestsBase { tf3.setAdjacentTaskFragment(null); final TaskFragment tf4 = createTaskFragmentWithActivity(task); // Stacked + companion => predict for previous activity below companion. // Stacked + next companion to top => 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); outPrevActivities.clear(); tf3.setCompanionTaskFragment(null); // Stacked + top companion to next but next one didn't => predict for previous activity. tf4.setCompanionTaskFragment(tf3); topAr = tf4.getTopMostActivity(); predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr, outPrevActivities); assertTrue(outPrevActivities.contains(tf3.getTopMostActivity())); assertTrue(predictable); } @Test Loading