Merge "Keep visible requested for transient hide activities" into udc-dev

This commit is contained in:
Riddle Hsu
2023-03-15 11:33:44 +00:00
committed by Android (Google) Code Review
5 changed files with 81 additions and 28 deletions

View File

@@ -1680,6 +1680,11 @@ class ActivityStarter {
targetTask.removeImmediately("bulky-task");
return START_ABORTED;
}
// When running transient transition, the transient launch target should keep on top.
// So disallow the transient hide activity to move itself to front, e.g. trampoline.
if (!mAvoidMoveToFront && r.mTransitionController.isTransientHide(targetTask)) {
mAvoidMoveToFront = true;
}
mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask());
}
@@ -1796,7 +1801,7 @@ class ActivityStarter {
// root-task to the will not update the focused root-task. If starting the new
// activity now allows the task root-task to be focusable, then ensure that we
// now update the focused root-task accordingly.
if (mTargetRootTask.isTopActivityFocusable()
if (!mAvoidMoveToFront && mTargetRootTask.isTopActivityFocusable()
&& !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
mTargetRootTask.moveToFront("startActivityInner");
}

View File

@@ -1012,6 +1012,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (isTopActivityLaunchedBehind()) {
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
final Task thisTask = asTask();
if (thisTask != null && mTransitionController.isTransientHide(thisTask)) {
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
boolean gotTranslucentFullscreen = false;
boolean gotTranslucentAdjacent = false;

View File

@@ -194,6 +194,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
*/
private ArrayMap<ActivityRecord, Task> mTransientLaunches = null;
/**
* The tasks that may be occluded by the transient activity. Assume the task stack is
* [Home, A(opaque), B(opaque), C(translucent)] (bottom to top), then A is the restore-below
* task, and [B, C] are the transient-hide tasks.
*/
private ArrayList<Task> mTransientHideTasks;
/** Custom activity-level animation options and callbacks. */
private TransitionInfo.AnimationOptions mOverrideOptions;
private IRemoteCallback mClientAnimationStartCallback = null;
@@ -265,35 +272,51 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelow) {
if (mTransientLaunches == null) {
mTransientLaunches = new ArrayMap<>();
mTransientHideTasks = new ArrayList<>();
}
mTransientLaunches.put(activity, restoreBelow);
setTransientLaunchToChanges(activity);
if (restoreBelow != null) {
final ChangeInfo info = mChanges.get(restoreBelow);
if (info != null) {
info.mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
// Collect all visible activities which can be occluded by the transient activity to
// make sure they are in the participants so their visibilities can be updated when
// finishing transition.
((WindowContainer<?>) restoreBelow.getParent()).forAllTasks(t -> {
if (t.isVisibleRequested() && !t.isAlwaysOnTop()
&& !t.getWindowConfiguration().tasksAreFloating()) {
if (t.isRootTask()) {
mTransientHideTasks.add(t);
}
if (t.isLeafTask()) {
t.forAllActivities(r -> {
if (r.isVisibleRequested()) {
collect(r);
}
});
}
}
return t == restoreBelow;
});
// Add FLAG_ABOVE_TRANSIENT_LAUNCH to the tree of transient-hide tasks,
// so ChangeInfo#hasChanged() can return true to report the transition info.
for (int i = mChanges.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = mChanges.keyAt(i);
if (wc.asTaskFragment() == null && wc.asActivityRecord() == null) continue;
if (isInTransientHide(wc)) {
mChanges.valueAt(i).mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
}
}
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
+ "transient-launch", mSyncId, activity);
}
boolean isTransientHide(@NonNull Task task) {
if (mTransientLaunches == null) return false;
for (int i = 0; i < mTransientLaunches.size(); ++i) {
if (mTransientLaunches.valueAt(i) == task) {
return true;
}
}
return false;
}
/** @return whether `wc` is a descendent of a transient-hide window. */
boolean isInTransientHide(@NonNull WindowContainer wc) {
if (mTransientLaunches == null) return false;
for (int i = 0; i < mTransientLaunches.size(); ++i) {
if (wc.isDescendantOf(mTransientLaunches.valueAt(i))) {
if (mTransientHideTasks == null) return false;
for (int i = mTransientHideTasks.size() - 1; i >= 0; --i) {
final Task task = mTransientHideTasks.get(i);
if (wc == task || wc.isDescendantOf(task)) {
return true;
}
}
@@ -816,6 +839,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
mController.mFinishingTransition = this;
if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
// The transient hide tasks could be occluded now, e.g. returning to home. So trigger
// the update to make the activities in the tasks invisible-requested, then the next
// step can continue to commit the visibility.
mController.mAtm.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
0 /* configChanges */, true /* preserveWindows */);
}
boolean hasParticipatedDisplay = false;
boolean hasVisibleTransientLaunch = false;
// Commit all going-invisible containers
@@ -1175,14 +1206,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// Record windowtokens (activity/wallpaper) that are expected to be visible after the
// transition animation. This will be used in finishTransition to prevent prematurely
// committing visibility.
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer wc = mParticipants.valueAt(i);
if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue;
// don't include transient launches, though, since those are only temporarily visible.
if (mTransientLaunches != null && wc.asActivityRecord() != null
&& mTransientLaunches.containsKey(wc.asActivityRecord())) continue;
mVisibleAtTransitionEndTokens.add(wc.asWindowToken());
// committing visibility. Skip transient launches since those are only temporarily visible.
if (mTransientLaunches == null) {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer wc = mParticipants.valueAt(i);
if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue;
mVisibleAtTransitionEndTokens.add(wc.asWindowToken());
}
}
// Take task snapshots before the animation so that we can capture IME before it gets

View File

@@ -366,11 +366,11 @@ class TransitionController {
}
boolean isTransientHide(@NonNull Task task) {
if (mCollectingTransition != null && mCollectingTransition.isTransientHide(task)) {
if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
return true;
}
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isTransientHide(task)) return true;
if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
}
return false;
}

View File

@@ -40,6 +40,7 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.isIndependent;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -54,6 +55,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -1400,7 +1402,13 @@ public class TransitionTests extends WindowTestsBase {
closeTransition.collectExistenceChange(activity1);
closeTransition.collectExistenceChange(task2);
closeTransition.collectExistenceChange(activity2);
closeTransition.setTransientLaunch(activity2, null /* restoreBelow */);
closeTransition.setTransientLaunch(activity2, task1);
final Transition.ChangeInfo task1ChangeInfo = closeTransition.mChanges.get(task1);
assertNotNull(task1ChangeInfo);
assertTrue(task1ChangeInfo.hasChanged());
final Transition.ChangeInfo activity1ChangeInfo = closeTransition.mChanges.get(activity1);
assertNotNull(activity1ChangeInfo);
assertTrue(activity1ChangeInfo.hasChanged());
activity1.setVisibleRequested(false);
activity2.setVisibleRequested(true);
@@ -1416,6 +1424,8 @@ public class TransitionTests extends WindowTestsBase {
verify(snapshotController, times(0)).recordSnapshot(eq(task1), eq(false));
enteringAnimReports.clear();
doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(),
anyInt(), anyBoolean(), anyBoolean());
final boolean[] wasInFinishingTransition = { false };
controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
@Override
@@ -1430,7 +1440,11 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(wasInFinishingTransition[0]);
assertNull(controller.mFinishingTransition);
assertTrue(activity2.isVisible());
assertEquals(ActivityTaskManagerService.APP_SWITCH_DISALLOW, mAtm.getBalAppSwitchesState());
// Because task1 is occluded by task2, finishTransition should make activity1 invisible.
assertFalse(activity1.isVisibleRequested());
assertFalse(activity1.isVisible());
assertFalse(activity1.app.hasActivityInVisibleTask());
verify(snapshotController, times(1)).recordSnapshot(eq(task1), eq(false));