Merge "Pause transient hide activities before resuming next" into udc-qpr-dev

This commit is contained in:
Riddle Hsu
2023-07-19 11:58:46 +00:00
committed by Android (Google) Code Review
5 changed files with 83 additions and 5 deletions

View File

@@ -1020,7 +1020,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final WindowContainer<?> parent = getParent();
final Task thisTask = asTask();
if (thisTask != null && parent.asTask() == null
&& mTransitionController.isTransientHide(thisTask)) {
&& mTransitionController.isTransientVisible(thisTask)) {
// Keep transient-hide root tasks visible. Non-root tasks still follow standard rule.
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}

View File

@@ -407,6 +407,36 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return false;
}
/** Returns {@code true} if the task should keep visible if this is a transient transition. */
boolean isTransientVisible(@NonNull Task task) {
if (mTransientLaunches == null) return false;
int occludedCount = 0;
final int numTransient = mTransientLaunches.size();
for (int i = numTransient - 1; i >= 0; --i) {
final Task transientRoot = mTransientLaunches.keyAt(i).getRootTask();
if (transientRoot == null) continue;
final WindowContainer<?> rootParent = transientRoot.getParent();
if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor
.mOpaqueActivityHelper.getOpaqueActivity(rootParent);
if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
occludedCount++;
}
}
if (occludedCount == numTransient) {
for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
// Keep transient activity visible until transition finished, so it won't pause
// with transient-hide tasks that may delay resuming the next top.
return true;
}
}
// Let transient-hide activities pause before transition is finished.
return false;
}
return isInTransientHide(task);
}
boolean canApplyDim(@NonNull Task task) {
if (mTransientLaunches == null) return true;
final Dimmer dimmer = task.getDimmer();

View File

@@ -477,15 +477,22 @@ class TransitionController {
if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
return true;
}
for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
if (mWaitingTransitions.get(i).isInTransientHide(task)) return true;
}
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
}
return false;
}
boolean isTransientVisible(@NonNull Task task) {
if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) {
return true;
}
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isTransientVisible(task)) return true;
}
return false;
}
boolean canApplyDim(@Nullable Task task) {
if (task == null) {
// Always allow non-activity window.

View File

@@ -656,7 +656,7 @@ public class RootTaskTests extends WindowTestsBase {
topSplitPrimary.getVisibility(null /* starting */));
// Make primary split root transient-hide.
spyOn(splitPrimary.mTransitionController);
doReturn(true).when(splitPrimary.mTransitionController).isTransientHide(
doReturn(true).when(splitPrimary.mTransitionController).isTransientVisible(
organizer.mPrimary);
// The split root and its top become visible.
assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,

View File

@@ -1483,6 +1483,47 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(enteringAnimReports.contains(activity2));
}
@Test
public void testIsTransientVisible() {
final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
.setVisible(false).build();
final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
.setVisible(false).build();
final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Task taskA = appA.getTask();
final Task taskB = appB.getTask();
final Task taskRecent = recent.getTask();
registerTestTransitionPlayer();
final TransitionController controller = mRootWindowContainer.mTransitionController;
final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
controller.moveToCollecting(transition);
transition.collect(recent);
transition.collect(taskA);
transition.setTransientLaunch(recent, taskA);
taskRecent.moveToFront("move-recent-to-front");
// During collecting and playing, the recent is on top so it is visible naturally.
// While B needs isTransientVisible to keep visibility because it is occluded by recents.
assertFalse(controller.isTransientVisible(taskB));
assertTrue(controller.isTransientVisible(taskA));
assertFalse(controller.isTransientVisible(taskRecent));
// Switch to playing state.
transition.onTransactionReady(transition.getSyncId(), mMockT);
assertTrue(controller.isTransientVisible(taskA));
// Switch to another task. For example, use gesture navigation to switch tasks.
taskB.moveToFront("move-b-to-front");
// The previous app (taskA) should be paused first so it loses transient visible. Because
// visually it is taskA -> taskB, the pause -> resume order should be the same.
assertFalse(controller.isTransientVisible(taskA));
// Keep the recent visible so there won't be 2 activities pausing at the same time. It is
// to avoid the latency to resume the current top, i.e. appB.
assertTrue(controller.isTransientVisible(taskRecent));
// The recent is paused after the transient transition is finished.
controller.finishTransition(transition);
assertFalse(controller.isTransientVisible(taskRecent));
}
@Test
public void testNotReadyPushPop() {
final TransitionController controller = new TestTransitionController(mAtm);