Move more utils into TransitionUtil

Move leash creation utilities and general queries

Bug: 267738124
Test: no logic change, existing tests pass
Change-Id: I684e66f295d0e9cb31a522bde77edc007aa2505e
This commit is contained in:
Evan Rosky
2023-03-01 18:26:09 -08:00
parent 6e1eb30543
commit d667c693c2
4 changed files with 223 additions and 223 deletions

View File

@@ -16,18 +16,40 @@
package com.android.wm.shell.util;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.RemoteAnimationTarget.MODE_CHANGING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
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_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.SparseBooleanArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import java.util.function.Predicate;
/** Various utility functions for transitions. */
public class TransitionUtil {
@@ -54,4 +76,189 @@ public class TransitionUtil {
return false;
}
/** Returns `true` if `change` is a wallpaper. */
public static boolean isWallpaper(TransitionInfo.Change change) {
return (change.getTaskInfo() == null)
&& change.hasFlags(FLAG_IS_WALLPAPER)
&& !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
}
/** Returns `true` if `change` is not an app window or wallpaper. */
public static boolean isNonApp(TransitionInfo.Change change) {
return (change.getTaskInfo() == null)
&& !change.hasFlags(FLAG_IS_WALLPAPER)
&& !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
}
/**
* 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.
*/
public static class LeafTaskFilter implements Predicate<TransitionInfo.Change> {
private final SparseBooleanArray mChildTaskTargets = new SparseBooleanArray();
@Override
public boolean test(TransitionInfo.Change change) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
// Children always come before parent since changes are in top-to-bottom z-order.
if ((taskInfo == null) || mChildTaskTargets.get(taskInfo.taskId)) {
// has children, so not a leaf. Skip.
return false;
}
if (taskInfo.hasParentTask()) {
mChildTaskTargets.put(taskInfo.parentTaskId, true);
}
return true;
}
}
private static int newModeToLegacyMode(int newMode) {
switch (newMode) {
case WindowManager.TRANSIT_OPEN:
case WindowManager.TRANSIT_TO_FRONT:
return MODE_OPENING;
case WindowManager.TRANSIT_CLOSE:
case WindowManager.TRANSIT_TO_BACK:
return MODE_CLOSING;
default:
return MODE_CHANGING;
}
}
/**
* Very similar to Transitions#setupAnimHierarchy but specialized for leashes.
*/
@SuppressLint("NewApi")
private static void setupLeash(@NonNull SurfaceControl leash,
@NonNull TransitionInfo.Change change, int layer,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
final boolean isOpening = TransitionUtil.isOpeningType(info.getType());
// Put animating stuff above this line and put static stuff below it.
int zSplitLine = info.getChanges().size();
// changes should be ordered top-to-bottom in z
final int mode = change.getMode();
t.reparent(leash, info.getRootLeash());
final Rect absBounds =
(mode == TRANSIT_OPEN) ? change.getEndAbsBounds() : change.getStartAbsBounds();
t.setPosition(leash, absBounds.left - info.getRootOffset().x,
absBounds.top - info.getRootOffset().y);
// Put all the OPEN/SHOW on top
if (TransitionUtil.isOpeningType(mode)) {
if (isOpening) {
t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
// if transferred, it should be left visible.
t.setAlpha(leash, 0.f);
}
} else {
// put on bottom and leave it visible
t.setLayer(leash, zSplitLine - layer);
}
} else if (TransitionUtil.isClosingType(mode)) {
if (isOpening) {
// put on bottom and leave visible
t.setLayer(leash, zSplitLine - layer);
} else {
// put on top
t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
}
} else { // CHANGE
t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
}
}
@SuppressLint("NewApi")
private static SurfaceControl createLeash(TransitionInfo info, TransitionInfo.Change change,
int order, SurfaceControl.Transaction t) {
// TODO: once we can properly sync transactions across process, then get rid of this leash.
if (change.getParent() != null && (change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
// Special case for wallpaper atm. Normally these are left alone; but, a quirk of
// making leashes means we have to handle them specially.
return change.getLeash();
}
SurfaceControl leashSurface = new SurfaceControl.Builder()
.setName(change.getLeash().toString() + "_transition-leash")
.setContainerLayer()
// Initial the surface visible to respect the visibility of the original surface.
.setHidden(false)
.setParent(info.getRootLeash())
.build();
// Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
setupLeash(leashSurface, change, info.getChanges().size() - order, info, t);
t.reparent(change.getLeash(), leashSurface);
t.setAlpha(change.getLeash(), 1.0f);
t.show(change.getLeash());
t.setPosition(change.getLeash(), 0, 0);
t.setLayer(change.getLeash(), 0);
return leashSurface;
}
/**
* Creates a new RemoteAnimationTarget from the provided change info
*/
public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
TransitionInfo info, SurfaceControl.Transaction t,
@Nullable ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
final SurfaceControl leash = createLeash(info, change, order, t);
if (leashMap != null) {
leashMap.put(change.getLeash(), leash);
}
return newTarget(change, order, leash);
}
/**
* Creates a new RemoteAnimationTarget from the provided change and leash
*/
public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
SurfaceControl leash) {
int taskId;
boolean isNotInRecents;
ActivityManager.RunningTaskInfo taskInfo;
WindowConfiguration windowConfiguration;
taskInfo = change.getTaskInfo();
if (taskInfo != null) {
taskId = taskInfo.taskId;
isNotInRecents = !taskInfo.isRunning;
windowConfiguration = taskInfo.configuration.windowConfiguration;
} else {
taskId = INVALID_TASK_ID;
isNotInRecents = true;
windowConfiguration = new WindowConfiguration();
}
Rect localBounds = new Rect(change.getEndAbsBounds());
localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
RemoteAnimationTarget target = new RemoteAnimationTarget(
taskId,
newModeToLegacyMode(change.getMode()),
// TODO: once we can properly sync transactions across process,
// then get rid of this leash.
leash,
(change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0,
null,
// TODO(shell-transitions): we need to send content insets? evaluate how its used.
new Rect(0, 0, 0, 0),
order,
null,
localBounds,
new Rect(change.getEndAbsBounds()),
windowConfiguration,
isNotInRecents,
null,
new Rect(change.getStartAbsBounds()),
taskInfo,
change.getAllowEnterPip(),
(change.getFlags() & FLAG_IS_DIVIDER_BAR) != 0
? TYPE_DOCK_DIVIDER : INVALID_WINDOW_TYPE
);
target.setWillShowImeOnTarget(
(change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0);
target.setRotationChange(change.getEndRotation() - change.getStartRotation());
return target;
}
}

View File

@@ -16,30 +16,9 @@
package com.android.systemui.shared.system;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.RemoteAnimationTarget.MODE_CHANGING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.SparseBooleanArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionInfo.Change;
@@ -53,156 +32,6 @@ import java.util.function.Predicate;
*/
public class RemoteAnimationTargetCompat {
private static int newModeToLegacyMode(int newMode) {
switch (newMode) {
case WindowManager.TRANSIT_OPEN:
case WindowManager.TRANSIT_TO_FRONT:
return MODE_OPENING;
case WindowManager.TRANSIT_CLOSE:
case WindowManager.TRANSIT_TO_BACK:
return MODE_CLOSING;
default:
return MODE_CHANGING;
}
}
/**
* Almost a copy of Transitions#setupStartState.
* TODO: remove when there is proper cross-process transaction sync.
*/
@SuppressLint("NewApi")
private static void setupLeash(@NonNull SurfaceControl leash,
@NonNull TransitionInfo.Change change, int layer,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
final boolean isOpening = TransitionUtil.isOpeningType(info.getType());
// Put animating stuff above this line and put static stuff below it.
int zSplitLine = info.getChanges().size();
// changes should be ordered top-to-bottom in z
final int mode = change.getMode();
t.reparent(leash, info.getRootLeash());
final Rect absBounds =
(mode == TRANSIT_OPEN) ? change.getEndAbsBounds() : change.getStartAbsBounds();
t.setPosition(leash, absBounds.left - info.getRootOffset().x,
absBounds.top - info.getRootOffset().y);
// Put all the OPEN/SHOW on top
if (TransitionUtil.isOpeningType(mode)) {
if (isOpening) {
t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
// if transferred, it should be left visible.
t.setAlpha(leash, 0.f);
}
} else {
// put on bottom and leave it visible
t.setLayer(leash, zSplitLine - layer);
}
} else if (TransitionUtil.isClosingType(mode)) {
if (isOpening) {
// put on bottom and leave visible
t.setLayer(leash, zSplitLine - layer);
} else {
// put on top
t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
}
} else { // CHANGE
t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
}
}
@SuppressLint("NewApi")
private static SurfaceControl createLeash(TransitionInfo info, TransitionInfo.Change change,
int order, SurfaceControl.Transaction t) {
// TODO: once we can properly sync transactions across process, then get rid of this leash.
if (change.getParent() != null && (change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
// Special case for wallpaper atm. Normally these are left alone; but, a quirk of
// making leashes means we have to handle them specially.
return change.getLeash();
}
SurfaceControl leashSurface = new SurfaceControl.Builder()
.setName(change.getLeash().toString() + "_transition-leash")
.setContainerLayer()
// Initial the surface visible to respect the visibility of the original surface.
.setHidden(false)
.setParent(info.getRootLeash())
.build();
// Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
setupLeash(leashSurface, change, info.getChanges().size() - order, info, t);
t.reparent(change.getLeash(), leashSurface);
t.setAlpha(change.getLeash(), 1.0f);
t.show(change.getLeash());
t.setPosition(change.getLeash(), 0, 0);
t.setLayer(change.getLeash(), 0);
return leashSurface;
}
/**
* Creates a new RemoteAnimationTarget from the provided change info
*/
public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
TransitionInfo info, SurfaceControl.Transaction t,
@Nullable ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
final SurfaceControl leash = createLeash(info, change, order, t);
if (leashMap != null) {
leashMap.put(change.getLeash(), leash);
}
return newTarget(change, order, leash);
}
/**
* Creates a new RemoteAnimationTarget from the provided change and leash
*/
public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
SurfaceControl leash) {
int taskId;
boolean isNotInRecents;
ActivityManager.RunningTaskInfo taskInfo;
WindowConfiguration windowConfiguration;
taskInfo = change.getTaskInfo();
if (taskInfo != null) {
taskId = taskInfo.taskId;
isNotInRecents = !taskInfo.isRunning;
windowConfiguration = taskInfo.configuration.windowConfiguration;
} else {
taskId = INVALID_TASK_ID;
isNotInRecents = true;
windowConfiguration = new WindowConfiguration();
}
Rect localBounds = new Rect(change.getEndAbsBounds());
localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
RemoteAnimationTarget target = new RemoteAnimationTarget(
taskId,
newModeToLegacyMode(change.getMode()),
// TODO: once we can properly sync transactions across process,
// then get rid of this leash.
leash,
(change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0,
null,
// TODO(shell-transitions): we need to send content insets? evaluate how its used.
new Rect(0, 0, 0, 0),
order,
null,
localBounds,
new Rect(change.getEndAbsBounds()),
windowConfiguration,
isNotInRecents,
null,
new Rect(change.getStartAbsBounds()),
taskInfo,
change.getAllowEnterPip(),
(change.getFlags() & FLAG_IS_DIVIDER_BAR) != 0
? TYPE_DOCK_DIVIDER : INVALID_WINDOW_TYPE
);
target.setWillShowImeOnTarget(
(change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0);
target.setRotationChange(change.getEndRotation() - change.getStartRotation());
return target;
}
/**
* Represents a TransitionInfo object as an array of old-style app targets
*
@@ -211,7 +40,7 @@ public class RemoteAnimationTargetCompat {
*/
public static RemoteAnimationTarget[] wrapApps(TransitionInfo info,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
return wrap(info, t, leashMap, new LeafTaskFilter());
return wrap(info, t, leashMap, new TransitionUtil.LeafTaskFilter());
}
/**
@@ -224,8 +53,8 @@ public class RemoteAnimationTargetCompat {
*/
public static RemoteAnimationTarget[] wrapNonApps(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
return wrap(info, t, leashMap, (change) ->
(wallpapers ? isWallpaper(change) : isNonApp(change)));
return wrap(info, t, leashMap, (change) -> (wallpapers
? TransitionUtil.isWallpaper(change) : TransitionUtil.isNonApp(change)));
}
private static RemoteAnimationTarget[] wrap(TransitionInfo info,
@@ -235,45 +64,10 @@ public class RemoteAnimationTargetCompat {
for (int i = 0; i < info.getChanges().size(); i++) {
TransitionInfo.Change change = info.getChanges().get(i);
if (filter.test(change)) {
out.add(newTarget(change, info.getChanges().size() - i, info, t, leashMap));
out.add(TransitionUtil.newTarget(
change, info.getChanges().size() - i, info, t, leashMap));
}
}
return out.toArray(new RemoteAnimationTarget[out.size()]);
}
/** Returns `true` if `change` is a wallpaper. */
public static boolean isWallpaper(Change change) {
return (change.getTaskInfo() == null)
&& change.hasFlags(FLAG_IS_WALLPAPER)
&& !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
}
/** Returns `true` if `change` is not an app window or wallpaper. */
public static boolean isNonApp(Change change) {
return (change.getTaskInfo() == null)
&& !change.hasFlags(FLAG_IS_WALLPAPER)
&& !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
}
/**
* 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.
*/
public static class LeafTaskFilter implements Predicate<Change> {
private final SparseBooleanArray mChildTaskTargets = new SparseBooleanArray();
@Override
public boolean test(Change change) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
// Children always come before parent since changes are in top-to-bottom z-order.
if ((taskInfo == null) || mChildTaskTargets.get(taskInfo.taskId)) {
// has children, so not a leaf. Skip.
return false;
}
if (taskInfo.hasParentTask()) {
mChildTaskTargets.put(taskInfo.parentTaskId, true);
}
return true;
}
}
}

View File

@@ -21,8 +21,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.newTarget;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -160,16 +158,15 @@ public class RemoteTransitionCompat {
final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
RemoteAnimationTargetCompat.LeafTaskFilter leafTaskFilter =
new RemoteAnimationTargetCompat.LeafTaskFilter();
TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter();
// About layering: we divide up the "layer space" into 3 regions (each the size of
// the change count). This lets us categorize things into above/below/between
// while maintaining their relative ordering.
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (RemoteAnimationTargetCompat.isWallpaper(change)) {
final RemoteAnimationTarget target = newTarget(change,
if (TransitionUtil.isWallpaper(change)) {
final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
// wallpapers go into the "below" layer space
info.getChanges().size() - i, info, t, mLeashMap);
wallpapers.add(target);
@@ -177,7 +174,7 @@ public class RemoteTransitionCompat {
t.setAlpha(target.leash, 1);
} else if (leafTaskFilter.test(change)) {
// start by putting everything into the "below" layer space.
final RemoteAnimationTarget target = newTarget(change,
final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
info.getChanges().size() - i, info, t, mLeashMap);
apps.add(target);
if (TransitionUtil.isClosingType(change.getMode())) {
@@ -217,8 +214,8 @@ public class RemoteTransitionCompat {
TransitionInfo.Change recentsOpening = null;
boolean foundRecentsClosing = false;
boolean hasChangingApp = false;
final RemoteAnimationTargetCompat.LeafTaskFilter leafTaskFilter =
new RemoteAnimationTargetCompat.LeafTaskFilter();
final TransitionUtil.LeafTaskFilter leafTaskFilter =
new TransitionUtil.LeafTaskFilter();
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -301,7 +298,8 @@ public class RemoteTransitionCompat {
int pausingIdx = TaskState.indexOf(mPausingTasks, change);
if (pausingIdx >= 0) {
// Something is showing/opening a previously-pausing app.
targets[i] = newTarget(change, layer, mPausingTasks.get(pausingIdx).mLeash);
targets[i] = TransitionUtil.newTarget(change, layer,
mPausingTasks.get(pausingIdx).mLeash);
mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
// Setup hides opening tasks initially, so make it visible again (since we
// are already showing it).
@@ -309,7 +307,7 @@ public class RemoteTransitionCompat {
t.setAlpha(change.getLeash(), 1.f);
} else {
// We are receiving new opening tasks, so convert to onTasksAppeared.
targets[i] = newTarget(change, layer, info, t, mLeashMap);
targets[i] = TransitionUtil.newTarget(change, layer, info, t, mLeashMap);
t.reparent(targets[i].leash, mInfo.getRootLeash());
t.setLayer(targets[i].leash, layer);
mOpeningTasks.add(new TaskState(change, targets[i].leash));

View File

@@ -49,6 +49,7 @@ import android.window.TransitionInfo;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.wm.shell.util.TransitionUtil;
import org.junit.Before;
import org.junit.Test;
@@ -120,7 +121,7 @@ public class RemoteTransitionTest extends SysuiTestCase {
change.setTaskInfo(createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_HOME));
change.setEndAbsBounds(endBounds);
change.setEndRelOffset(0, 0);
RemoteAnimationTarget wrapped = RemoteAnimationTargetCompat.newTarget(
RemoteAnimationTarget wrapped = TransitionUtil.newTarget(
change, 0 /* order */, tinfo, mock(SurfaceControl.Transaction.class), null);
assertEquals(ACTIVITY_TYPE_HOME, wrapped.windowConfiguration.getActivityType());
assertEquals(new Rect(0, 0, 100, 140), wrapped.localBounds);