Merge "Add to-top reporting to transitions" into udc-dev

This commit is contained in:
Evan Rosky
2023-03-31 16:50:11 +00:00
committed by Android (Google) Code Review
9 changed files with 142 additions and 10 deletions

View File

@@ -149,8 +149,11 @@ public final class TransitionInfo implements Parcelable {
/** The task is launching behind home. */
public static final int FLAG_TASK_LAUNCHING_BEHIND = 1 << 19;
/** The task became the top-most task even if it didn't change visibility. */
public static final int FLAG_MOVED_TO_TOP = 1 << 20;
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
public static final int FLAG_FIRST_CUSTOM = 1 << 20;
public static final int FLAG_FIRST_CUSTOM = 1 << 21;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -179,6 +182,7 @@ public final class TransitionInfo implements Parcelable {
FLAG_BACK_GESTURE_ANIMATED,
FLAG_NO_ANIMATION,
FLAG_TASK_LAUNCHING_BEHIND,
FLAG_MOVED_TO_TOP,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}

View File

@@ -456,7 +456,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
cancel(mWillFinishToHome);
return;
}
hasChangingApp = true;
// Don't consider order-only changes as changing apps.
if (!TransitionUtil.isOrderOnly(change)) {
hasChangingApp = true;
}
}
}
if (hasChangingApp && foundRecentsClosing) {
@@ -484,13 +487,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
}
boolean didMergeThings = false;
if (closingTasks != null) {
// Cancelling a task-switch. Move the tasks back to mPausing from mOpening
// Potentially cancelling a task-switch. Move the tasks back to mPausing if they
// are in mOpening.
for (int i = 0; i < closingTasks.size(); ++i) {
final TransitionInfo.Change change = closingTasks.get(i);
int openingIdx = TaskState.indexOf(mOpeningTasks, change);
if (openingIdx < 0) {
Slog.e(TAG, "Back to existing recents animation from an unrecognized "
+ "task: " + change.getTaskInfo().taskId);
Slog.w(TAG, "Closing a task that wasn't opening, this may be split or"
+ " something unexpected: " + change.getTaskInfo().taskId);
continue;
}
mPausingTasks.add(mOpeningTasks.remove(openingIdx));

View File

@@ -301,8 +301,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return true;
}
// check if no-animation and skip animation if so.
if (Transitions.isAllNoAnimation(info)) {
// Early check if the transition doesn't warrant an animation.
if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)) {
startTransaction.apply();
finishTransaction.apply();
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);

View File

@@ -516,6 +516,16 @@ public class Transitions implements RemoteCallable<Transitions> {
return hasNoAnimation;
}
/**
* Check if all changes in this transition are only ordering changes. If so, we won't animate.
*/
static boolean isAllOrderOnly(TransitionInfo info) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
if (!TransitionUtil.isOrderOnly(info.getChanges().get(i))) return false;
}
return true;
}
@VisibleForTesting
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {

View File

@@ -31,6 +31,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -90,6 +91,15 @@ public class TransitionUtil {
&& !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
}
/** Returns `true` if `change` is only re-ordering. */
public static boolean isOrderOnly(TransitionInfo.Change change) {
return change.getMode() == TRANSIT_CHANGE
&& (change.getFlags() & FLAG_MOVED_TO_TOP) != 0
&& change.getStartAbsBounds().equals(change.getEndAbsBounds())
&& (change.getLastParent() == null
|| change.getLastParent().equals(change.getParent()));
}
/**
* Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
* MUST call `test` in the same order that the changes appear in the TransitionInfo.

View File

@@ -63,6 +63,7 @@ public class RemoteAnimationTargetCompat {
final ArrayList<RemoteAnimationTarget> out = new ArrayList<>();
for (int i = 0; i < info.getChanges().size(); i++) {
TransitionInfo.Change change = info.getChanges().get(i);
if (TransitionUtil.isOrderOnly(change)) continue;
if (filter.test(change)) {
out.add(TransitionUtil.newTarget(
change, info.getChanges().size() - i, info, t, leashMap));

View File

@@ -4687,6 +4687,7 @@ class Task extends TaskFragment {
if (!isAttached()) {
return;
}
mTransitionController.collect(this);
final TaskDisplayArea taskDisplayArea = getDisplayArea();

View File

@@ -50,6 +50,7 @@ import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
@@ -182,6 +183,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
/** The displays that this transition is running on. */
private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
/**
* The (non alwaysOnTop) tasks which were on-top of their display before the transition. If
* tasks are nested, all the tasks that are parents of the on-top task are also included.
*/
private final ArrayList<Task> mOnTopTasksStart = new ArrayList<>();
/**
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
* the transition animation.
@@ -515,6 +522,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mParticipants.add(wc);
if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
mTargetDisplays.add(wc.getDisplayContent());
addOnTopTasks(wc.getDisplayContent(), mOnTopTasksStart);
}
if (info.mShowWallpaper) {
// Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
@@ -526,6 +534,27 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
}
/** Adds the top non-alwaysOnTop tasks within `task` to `out`. */
private static void addOnTopTasks(Task task, ArrayList<Task> out) {
for (int i = task.getChildCount() - 1; i >= 0; --i) {
final Task child = task.getChildAt(i).asTask();
if (child == null) return;
if (child.getWindowConfiguration().isAlwaysOnTop()) continue;
out.add(child);
addOnTopTasks(child, out);
break;
}
}
/** Get the top non-alwaysOnTop leaf task on the display `dc`. */
private static void addOnTopTasks(DisplayContent dc, ArrayList<Task> out) {
final Task topNotAlwaysOnTop = dc.getRootTask(
t -> !t.getWindowConfiguration().isAlwaysOnTop());
if (topNotAlwaysOnTop == null) return;
out.add(topNotAlwaysOnTop);
addOnTopTasks(topNotAlwaysOnTop, out);
}
/**
* Records wc as changing its state of existence during this transition. For example, a new
* task is considered an existence change while moving a task to front is not. wc is added
@@ -1000,11 +1029,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
InsetsControlTarget prevImeTarget = dc.getImeTarget(
DisplayContent.IME_TARGET_CONTROL);
InsetsControlTarget newImeTarget = null;
TaskDisplayArea transientTDA = null;
// Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
// so re-compute in case the IME target is changed after transition.
for (int t = 0; t < mTransientLaunches.size(); ++t) {
if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) {
newImeTarget = dc.computeImeTarget(true /* updateImeTarget */);
transientTDA = mTransientLaunches.keyAt(i).getTaskDisplayArea();
break;
}
}
@@ -1015,6 +1046,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
InputMethodManagerInternal.get().updateImeWindowStatus(
false /* disableImeIcon */);
}
// An uncommitted transient launch can leave incomplete lifecycles if visibilities
// didn't change (eg. re-ordering with translucent tasks will leave launcher
// in RESUMED state), so force an update here.
if (!hasVisibleTransientLaunch && transientTDA != null) {
transientTDA.pauseBackTasks(null /* resuming */);
}
}
dc.removeImeSurfaceImmediately();
dc.handleCompleteDeferredRemoval();
@@ -1140,6 +1177,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
// Check whether the participants were animated from back navigation.
mController.mAtm.mBackNavigationController.onTransactionReady(this);
collectOrderChanges();
// Resolve the animating targets from the participants.
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
@@ -1291,6 +1331,27 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
info.releaseAnimSurfaces();
}
/** Collect tasks which moved-to-top but didn't change otherwise. */
@VisibleForTesting
void collectOrderChanges() {
if (mOnTopTasksStart.isEmpty()) return;
final ArrayList<Task> onTopTasksEnd = new ArrayList<>();
for (int i = 0; i < mTargetDisplays.size(); ++i) {
addOnTopTasks(mTargetDisplays.get(i), onTopTasksEnd);
}
for (int i = 0; i < onTopTasksEnd.size(); ++i) {
final Task task = onTopTasksEnd.get(i);
if (mOnTopTasksStart.contains(task)) continue;
mParticipants.add(task);
int changeIdx = mChanges.indexOfKey(task);
if (changeIdx < 0) {
mChanges.put(task, new ChangeInfo(task));
changeIdx = mChanges.indexOfKey(task);
}
mChanges.valueAt(changeIdx).mFlags |= ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP;
}
}
private void postCleanupOnFailure() {
mController.mAtm.mH.post(() -> {
synchronized (mController.mAtm.mGlobalLock) {
@@ -2246,13 +2307,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
*/
private static final int FLAG_CHANGE_YES_ANIMATION = 0x10;
/** Whether this change's container moved to the top. */
private static final int FLAG_CHANGE_MOVED_TO_TOP = 0x20;
@IntDef(prefix = { "FLAG_" }, value = {
FLAG_NONE,
FLAG_SEAMLESS_ROTATION,
FLAG_TRANSIENT_LAUNCH,
FLAG_ABOVE_TRANSIENT_LAUNCH,
FLAG_CHANGE_NO_ANIMATION,
FLAG_CHANGE_YES_ANIMATION
FLAG_CHANGE_YES_ANIMATION,
FLAG_CHANGE_MOVED_TO_TOP
})
@Retention(RetentionPolicy.SOURCE)
@interface Flag {}
@@ -2283,7 +2348,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
int mDisplayId = -1;
@ActivityInfo.Config int mKnownConfigChanges;
/** These are just extra info. They aren't used for change-detection. */
/** Extra information about this change. */
@Flag int mFlags = FLAG_NONE;
/** Snapshot surface and luma, if relevant. */
@@ -2335,7 +2400,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
|| (mWindowingMode != 0 && mContainer.getWindowingMode() != mWindowingMode)
|| !mContainer.getBounds().equals(mAbsoluteBounds)
|| mRotation != mContainer.getWindowConfiguration().getRotation()
|| mDisplayId != getDisplayId(mContainer);
|| mDisplayId != getDisplayId(mContainer)
|| (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0;
}
@TransitionInfo.TransitionMode
@@ -2436,6 +2502,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
&& (mFlags & FLAG_CHANGE_YES_ANIMATION) == 0) {
flags |= FLAG_NO_ANIMATION;
}
if ((mFlags & FLAG_CHANGE_MOVED_TO_TOP) != 0) {
flags |= FLAG_MOVED_TO_TOP;
}
return flags;
}

View File

@@ -1885,6 +1885,39 @@ public class TransitionTests extends WindowTestsBase {
assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
}
@Test
public void testMoveToTopWhileVisible() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
final ArraySet<WindowContainer> participants = transition.mParticipants;
// Start with taskB on top and taskA on bottom but both visible.
final Task rootTaskA = createTask(mDisplayContent);
final Task leafTaskA = createTaskInRootTask(rootTaskA, 0 /* userId */);
final Task taskB = createTask(mDisplayContent);
leafTaskA.setVisibleRequested(true);
taskB.setVisibleRequested(true);
// manually collect since this is a test transition and not known by transitionController.
transition.collect(leafTaskA);
rootTaskA.moveToFront("test", leafTaskA);
// All the tasks were already visible, so there shouldn't be any changes
ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
participants, changes);
assertTrue(targets.isEmpty());
// After collecting order changes, it should recognize that a task moved to top.
transition.collectOrderChanges();
targets = Transition.calculateTargets(participants, changes);
assertEquals(1, targets.size());
// Make sure the flag is set
final TransitionInfo info = Transition.calculateTransitionInfo(
transition.mType, 0 /* flags */, targets, mMockT);
assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
}
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {