summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/window/TransitionInfo.java203
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt15
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java3
-rw-r--r--services/core/java/com/android/server/wm/Transition.java159
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java41
13 files changed, 388 insertions, 123 deletions
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index b8bd7032f678..4c482460543a 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -24,6 +24,7 @@ import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -58,6 +59,7 @@ import java.util.List;
* @hide
*/
public final class TransitionInfo implements Parcelable {
+ private static final String TAG = "TransitionInfo";
/**
* Modes are only a sub-set of all the transit-types since they are per-container
@@ -184,9 +186,7 @@ public final class TransitionInfo implements Parcelable {
private final @TransitionType int mType;
private final @TransitionFlags int mFlags;
private final ArrayList<Change> mChanges = new ArrayList<>();
-
- private SurfaceControl mRootLeash;
- private final Point mRootOffset = new Point();
+ private final ArrayList<Root> mRoots = new ArrayList<>();
private AnimationOptions mOptions;
@@ -200,10 +200,7 @@ public final class TransitionInfo implements Parcelable {
mType = in.readInt();
mFlags = in.readInt();
in.readTypedList(mChanges, Change.CREATOR);
- mRootLeash = new SurfaceControl();
- mRootLeash.readFromParcel(in);
- mRootLeash.setUnreleasedWarningCallSite("TransitionInfo");
- mRootOffset.readFromParcel(in);
+ in.readTypedList(mRoots, Root.CREATOR);
mOptions = in.readTypedObject(AnimationOptions.CREATOR);
}
@@ -213,8 +210,7 @@ public final class TransitionInfo implements Parcelable {
dest.writeInt(mType);
dest.writeInt(mFlags);
dest.writeTypedList(mChanges);
- mRootLeash.writeToParcel(dest, flags);
- mRootOffset.writeToParcel(dest, flags);
+ dest.writeTypedList(mRoots, flags);
dest.writeTypedObject(mOptions, flags);
}
@@ -238,10 +234,15 @@ public final class TransitionInfo implements Parcelable {
return 0;
}
- /** @see #getRootLeash() */
- public void setRootLeash(@NonNull SurfaceControl leash, int offsetLeft, int offsetTop) {
- mRootLeash = leash;
- mRootOffset.set(offsetLeft, offsetTop);
+ /** @see #getRoot */
+ public void addRootLeash(int displayId, @NonNull SurfaceControl leash,
+ int offsetLeft, int offsetTop) {
+ mRoots.add(new Root(displayId, leash, offsetLeft, offsetTop));
+ }
+
+ /** @see #getRoot */
+ public void addRoot(Root other) {
+ mRoots.add(other);
}
public void setAnimationOptions(AnimationOptions options) {
@@ -257,23 +258,52 @@ public final class TransitionInfo implements Parcelable {
}
/**
+ * @return The number of animation roots. Most transitions should have 1, but there may be more
+ * in some cases (such as a transition spanning multiple displays).
+ */
+ public int getRootCount() {
+ return mRoots.size();
+ }
+
+ /**
+ * @return the transition-root at a specific index.
+ */
+ @NonNull
+ public Root getRoot(int idx) {
+ return mRoots.get(idx);
+ }
+
+ /**
+ * @return the index of the transition-root associated with `displayId` or -1 if not found.
+ */
+ public int findRootIndex(int displayId) {
+ for (int i = 0; i < mRoots.size(); ++i) {
+ if (mRoots.get(i).mDisplayId == displayId) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
* @return a surfacecontrol that can serve as a parent surfacecontrol for all the changing
* participants to animate within. This will generally be placed at the highest-z-order
* shared ancestor of all participants. While this is non-null, it's possible for the rootleash
* to be invalid if the transition is a no-op.
+ *
+ * @deprecated Use {@link #getRoot} instead. This call assumes there is only one root.
*/
+ @Deprecated
@NonNull
public SurfaceControl getRootLeash() {
- if (mRootLeash == null) {
- throw new IllegalStateException("Trying to get a leash which wasn't set");
+ if (mRoots.isEmpty()) {
+ throw new IllegalStateException("Trying to get a root leash from a no-op transition.");
}
- return mRootLeash;
- }
-
- /** @return the offset (relative to the screen) of the root leash. */
- @NonNull
- public Point getRootOffset() {
- return mRootOffset;
+ if (mRoots.size() > 1) {
+ android.util.Log.e(TAG, "Assuming one animation root when there are more.",
+ new Throwable());
+ }
+ return mRoots.get(0).mLeash;
}
public AnimationOptions getAnimationOptions() {
@@ -320,8 +350,15 @@ public final class TransitionInfo implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("{t=" + transitTypeToString(mType) + " f=0x" + Integer.toHexString(mFlags)
- + " ro=" + mRootOffset + " c=[");
+ sb.append("{t=").append(transitTypeToString(mType)).append(" f=0x")
+ .append(Integer.toHexString(mFlags)).append(" r=[");
+ for (int i = 0; i < mRoots.size(); ++i) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(mRoots.get(i).mDisplayId).append("@").append(mRoots.get(i).mOffset);
+ }
+ sb.append("] c=[");
for (int i = 0; i < mChanges.size(); ++i) {
if (i > 0) {
sb.append(',');
@@ -448,8 +485,8 @@ public final class TransitionInfo implements Parcelable {
c.mSnapshot = null;
}
}
- if (mRootLeash != null) {
- mRootLeash.release();
+ for (int i = 0; i < mRoots.size(); ++i) {
+ mRoots.get(i).mLeash.release();
}
}
@@ -476,10 +513,11 @@ public final class TransitionInfo implements Parcelable {
for (int i = 0; i < mChanges.size(); ++i) {
out.mChanges.add(mChanges.get(i).localRemoteCopy());
}
- out.mRootLeash = mRootLeash != null ? new SurfaceControl(mRootLeash, "localRemote") : null;
+ for (int i = 0; i < mRoots.size(); ++i) {
+ out.mRoots.add(mRoots.get(i).localRemoteCopy());
+ }
// Doesn't have any native stuff, so no need for actual copy
out.mOptions = mOptions;
- out.mRootOffset.set(mRootOffset);
return out;
}
@@ -496,6 +534,8 @@ public final class TransitionInfo implements Parcelable {
private final Point mEndRelOffset = new Point();
private ActivityManager.RunningTaskInfo mTaskInfo = null;
private boolean mAllowEnterPip;
+ private int mStartDisplayId = INVALID_DISPLAY;
+ private int mEndDisplayId = INVALID_DISPLAY;
private @Surface.Rotation int mStartRotation = ROTATION_UNDEFINED;
private @Surface.Rotation int mEndRotation = ROTATION_UNDEFINED;
/**
@@ -526,6 +566,8 @@ public final class TransitionInfo implements Parcelable {
mEndRelOffset.readFromParcel(in);
mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
mAllowEnterPip = in.readBoolean();
+ mStartDisplayId = in.readInt();
+ mEndDisplayId = in.readInt();
mStartRotation = in.readInt();
mEndRotation = in.readInt();
mEndFixedRotation = in.readInt();
@@ -546,6 +588,8 @@ public final class TransitionInfo implements Parcelable {
out.mEndRelOffset.set(mEndRelOffset);
out.mTaskInfo = mTaskInfo;
out.mAllowEnterPip = mAllowEnterPip;
+ out.mStartDisplayId = mStartDisplayId;
+ out.mEndDisplayId = mEndDisplayId;
out.mStartRotation = mStartRotation;
out.mEndRotation = mEndRotation;
out.mEndFixedRotation = mEndFixedRotation;
@@ -608,6 +652,12 @@ public final class TransitionInfo implements Parcelable {
}
/** Sets the start and end rotation of this container. */
+ public void setDisplayId(int start, int end) {
+ mStartDisplayId = start;
+ mEndDisplayId = end;
+ }
+
+ /** Sets the start and end rotation of this container. */
public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) {
mStartRotation = start;
mEndRotation = end;
@@ -725,6 +775,14 @@ public final class TransitionInfo implements Parcelable {
return mAllowEnterPip;
}
+ public int getStartDisplayId() {
+ return mStartDisplayId;
+ }
+
+ public int getEndDisplayId() {
+ return mEndDisplayId;
+ }
+
@Surface.Rotation
public int getStartRotation() {
return mStartRotation;
@@ -776,6 +834,8 @@ public final class TransitionInfo implements Parcelable {
mEndRelOffset.writeToParcel(dest, flags);
dest.writeTypedObject(mTaskInfo, flags);
dest.writeBoolean(mAllowEnterPip);
+ dest.writeInt(mStartDisplayId);
+ dest.writeInt(mEndDisplayId);
dest.writeInt(mStartRotation);
dest.writeInt(mEndRotation);
dest.writeInt(mEndFixedRotation);
@@ -822,6 +882,11 @@ public final class TransitionInfo implements Parcelable {
if (mEndRelOffset.x != 0 || mEndRelOffset.y != 0) {
sb.append(" eo="); sb.append(mEndRelOffset);
}
+ sb.append(" d=");
+ if (mStartDisplayId != mEndDisplayId) {
+ sb.append(mStartDisplayId).append("->");
+ }
+ sb.append(mEndDisplayId);
if (mStartRotation != mEndRotation) {
sb.append(" r="); sb.append(mStartRotation);
sb.append("->"); sb.append(mEndRotation);
@@ -1108,4 +1173,86 @@ public final class TransitionInfo implements Parcelable {
};
}
}
+
+ /**
+ * An animation root in a transition. There is one of these for each display that contains
+ * participants. It will be placed, in z-order, right above the top-most participant and at the
+ * same position in the hierarchy. As a result, if all participants are animating within a
+ * part of the screen, the root-leash will only be in that part of the screen. In these cases,
+ * it's relative position (from the screen) is stored in {@link Root#getOffset}.
+ */
+ public static final class Root implements Parcelable {
+ private final int mDisplayId;
+ private final SurfaceControl mLeash;
+ private final Point mOffset = new Point();
+
+ public Root(int displayId, @NonNull SurfaceControl leash, int offsetLeft, int offsetTop) {
+ mDisplayId = displayId;
+ mLeash = leash;
+ mOffset.set(offsetLeft, offsetTop);
+ }
+
+ private Root(Parcel in) {
+ mDisplayId = in.readInt();
+ mLeash = new SurfaceControl();
+ mLeash.readFromParcel(in);
+ mLeash.setUnreleasedWarningCallSite("TransitionInfo.Root");
+ mOffset.readFromParcel(in);
+ }
+
+ private Root localRemoteCopy() {
+ return new Root(mDisplayId, new SurfaceControl(mLeash, "localRemote"),
+ mOffset.x, mOffset.y);
+ }
+
+ /** @return the id of the display this root is on. */
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /** @return the root's leash. Surfaces should be parented to this while animating. */
+ @NonNull
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+
+ /** @return the offset (relative to its screen) of the root leash. */
+ @NonNull
+ public Point getOffset() {
+ return mOffset;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mDisplayId);
+ mLeash.writeToParcel(dest, flags);
+ mOffset.writeToParcel(dest, flags);
+ }
+
+ @NonNull
+ public static final Creator<Root> CREATOR =
+ new Creator<Root>() {
+ @Override
+ public Root createFromParcel(Parcel in) {
+ return new Root(in);
+ }
+
+ @Override
+ public Root[] newArray(int size) {
+ return new Root[size];
+ }
+ };
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return mDisplayId + "@" + mOffset + ":" + mLeash;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 87cf6559a0f0..45bb73bdc0d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -489,10 +489,11 @@ public class PipTransition extends PipTransitionController {
// Reparent the pip leash to the root with max layer so that we can animate it outside of
// parent crop, and make sure it is not covered by other windows.
final SurfaceControl pipLeash = pipChange.getLeash();
- startTransaction.reparent(pipLeash, info.getRootLeash());
+ final int rootIdx = TransitionUtil.rootIndexFor(pipChange, info);
+ startTransaction.reparent(pipLeash, info.getRoot(rootIdx).getLeash());
startTransaction.setLayer(pipLeash, Integer.MAX_VALUE);
// Note: because of this, the bounds to animate should be translated to the root coordinate.
- final Point offset = info.getRootOffset();
+ final Point offset = info.getRoot(rootIdx).getOffset();
final Rect currentBounds = mPipBoundsState.getBounds();
currentBounds.offset(-offset.x, -offset.y);
startTransaction.setPosition(pipLeash, currentBounds.left, currentBounds.top);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index e1c089550c2d..e09c3c9e4d3f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -37,6 +37,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
@@ -128,6 +129,7 @@ class SplitScreenTransitions {
final int mode = info.getChanges().get(i).getMode();
if (mode == TRANSIT_CHANGE) {
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
if (change.getParent() != null) {
// This is probably reparented, so we want the parent to be immediately visible
final TransitionInfo.Change parentChange = info.getChange(change.getParent());
@@ -135,7 +137,7 @@ class SplitScreenTransitions {
t.setAlpha(parentChange.getLeash(), 1.f);
// and then animate this layer outside the parent (since, for example, this is
// the home task animating from fullscreen to part-screen).
- t.reparent(leash, info.getRootLeash());
+ t.reparent(leash, info.getRoot(rootIdx).getLeash());
t.setLayer(leash, info.getChanges().size() - i);
// build the finish reparent/reposition
mFinishTransaction.reparent(leash, parentChange.getLeash());
@@ -145,8 +147,9 @@ class SplitScreenTransitions {
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
final Rect endBounds = new Rect(change.getEndAbsBounds());
- startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
- endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
+ final Point rootOffset = info.getRoot(rootIdx).getOffset();
+ startBounds.offset(-rootOffset.x, -rootOffset.y);
+ endBounds.offset(-rootOffset.x, -rootOffset.y);
startExampleResizeAnimation(leash, startBounds, endBounds);
}
boolean isRootOrSplitSideRoot = change.getParent() == null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 75112b62c1c6..2e864483bf1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -179,7 +179,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
out.getChanges().add(info.getChanges().get(i));
}
}
- out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y);
+ for (int i = 0; i < info.getRootCount(); ++i) {
+ out.addRoot(info.getRoot(i));
+ }
out.setAnimationOptions(info.getAnimationOptions());
return out;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index f66c26bb87e4..63c7969291a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -383,9 +383,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
continue;
}
// No default animation for this, so just update bounds/position.
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
startTransaction.setPosition(change.getLeash(),
- change.getEndAbsBounds().left - info.getRootOffset().x,
- change.getEndAbsBounds().top - info.getRootOffset().y);
+ change.getEndAbsBounds().left - info.getRoot(rootIdx).getOffset().x,
+ change.getEndAbsBounds().top - info.getRoot(rootIdx).getOffset().y);
// Seamless display transition doesn't need to animate.
if (isSeamlessDisplayChange) continue;
if (isTask || (change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)
@@ -474,8 +475,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
if (backgroundColorForTransition != 0) {
- addBackgroundToTransition(info.getRootLeash(), backgroundColorForTransition,
- startTransaction, finishTransaction);
+ for (int i = 0; i < info.getRootCount(); ++i) {
+ addBackgroundToTransition(info.getRoot(i).getLeash(), backgroundColorForTransition,
+ startTransaction, finishTransaction);
+ }
}
if (postStartTransactionCallbacks.size() > 0) {
@@ -520,8 +523,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private void startRotationAnimation(SurfaceControl.Transaction startTransaction,
TransitionInfo.Change change, TransitionInfo info, int animHint,
ArrayList<Animator> animations, Runnable onAnimFinish) {
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash(), animHint);
+ mTransactionPool, startTransaction, change, info.getRoot(rootIdx).getLeash(),
+ animHint);
// The rotation animation may consist of 3 animations: fade-out screenshot, fade-in real
// content, and background color. The item of "animGroup" will be removed if the sub
// animation is finished. Then if the list becomes empty, the rotation animation is done.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 27b82c08d803..039bde95815e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -75,6 +75,7 @@ import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
import java.util.Arrays;
@@ -415,8 +416,8 @@ public class Transitions implements RemoteCallable<Transitions> {
private static void setupAnimHierarchy(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
boolean isOpening = isOpeningType(info.getType());
- if (info.getRootLeash().isValid()) {
- t.show(info.getRootLeash());
+ for (int i = 0; i < info.getRootCount(); ++i) {
+ t.show(info.getRoot(i).getLeash());
}
final int numChanges = info.getChanges().size();
// Put animating stuff above this line and put static stuff below it.
@@ -434,10 +435,12 @@ public class Transitions implements RemoteCallable<Transitions> {
boolean hasParent = change.getParent() != null;
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
if (!hasParent) {
- t.reparent(leash, info.getRootLeash());
- t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
- change.getStartAbsBounds().top - info.getRootOffset().y);
+ t.reparent(leash, info.getRoot(rootIdx).getLeash());
+ t.setPosition(leash,
+ change.getStartAbsBounds().left - info.getRoot(rootIdx).getOffset().x,
+ change.getStartAbsBounds().top - info.getRoot(rootIdx).getOffset().y);
}
final int layer;
// Put all the OPEN/SHOW on top
@@ -532,12 +535,6 @@ public class Transitions implements RemoteCallable<Transitions> {
if (info.getType() == TRANSIT_SLEEP) {
if (activeIdx > 0) {
- if (!info.getRootLeash().isValid()) {
- // Shell has some debug settings which makes calling binders with invalid
- // surfaces crash, so replace it with a "real" one.
- info.setRootLeash(new SurfaceControl.Builder().setName("Invalid")
- .setContainerLayer().build(), 0, 0);
- }
// Sleep starts a process of forcing all prior transitions to finish immediately
finishForSleep(null /* forceFinish */);
return;
@@ -546,10 +543,10 @@ public class Transitions implements RemoteCallable<Transitions> {
// Allow to notify keyguard un-occluding state to KeyguardService, which can happen while
// screen-off, so there might no visibility change involved.
- if (!info.getRootLeash().isValid() && info.getType() != TRANSIT_KEYGUARD_UNOCCLUDE) {
- // Invalid root-leash implies that the transition is empty/no-op, so just do
+ if (info.getRootCount() == 0 && info.getType() != TRANSIT_KEYGUARD_UNOCCLUDE) {
+ // No root-leashes implies that the transition is empty/no-op, so just do
// housekeeping and return.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots (%s): %s",
transitionToken, info);
onAbort(active);
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index 8c6e1e7f5f1b..7595c9617709 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -139,11 +139,12 @@ public class TransitionUtil {
// changes should be ordered top-to-bottom in z
final int mode = change.getMode();
- t.reparent(leash, info.getRootLeash());
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
+ t.reparent(leash, info.getRoot(rootIdx).getLeash());
final Rect absBounds =
(mode == TRANSIT_OPEN) ? change.getEndAbsBounds() : change.getStartAbsBounds();
- t.setPosition(leash, absBounds.left - info.getRootOffset().x,
- absBounds.top - info.getRootOffset().y);
+ t.setPosition(leash, absBounds.left - info.getRoot(rootIdx).getOffset().x,
+ absBounds.top - info.getRoot(rootIdx).getOffset().y);
// Put all the OPEN/SHOW on top
if (TransitionUtil.isOpeningType(mode)) {
@@ -179,12 +180,13 @@ public class TransitionUtil {
// making leashes means we have to handle them specially.
return change.getLeash();
}
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
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())
+ .setParent(info.getRoot(rootIdx).getLeash())
.build();
// Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
setupLeash(leashSurface, change, info.getChanges().size() - order, info, t);
@@ -261,4 +263,18 @@ public class TransitionUtil {
target.setRotationChange(change.getEndRotation() - change.getStartRotation());
return target;
}
+
+ /**
+ * Finds the "correct" root idx for a change. The change's end display is prioritized, then
+ * the start display. If there is no display, it will fallback on the 0th root in the
+ * transition. There MUST be at-least 1 root in the transition (ie. it's not a no-op).
+ */
+ public static int rootIndexFor(@NonNull TransitionInfo.Change change,
+ @NonNull TransitionInfo info) {
+ int rootIdx = info.findRootIndex(change.getEndDisplayId());
+ if (rootIdx >= 0) return rootIdx;
+ rootIdx = info.findRootIndex(change.getStartDisplayId());
+ if (rootIdx >= 0) return rootIdx;
+ return 0;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java
index 35c374ddd974..26b787fa836c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java
@@ -30,6 +30,7 @@ import android.window.TransitionInfo;
*/
public class TransitionInfoBuilder {
final TransitionInfo mInfo;
+ static final int DISPLAY_ID = 0;
public TransitionInfoBuilder(@WindowManager.TransitionType int type) {
this(type, 0 /* flags */);
@@ -38,7 +39,7 @@ public class TransitionInfoBuilder {
public TransitionInfoBuilder(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags) {
mInfo = new TransitionInfo(type, flags);
- mInfo.setRootLeash(createMockSurface(true /* valid */), 0, 0);
+ mInfo.addRootLeash(DISPLAY_ID, createMockSurface(true /* valid */), 0, 0);
}
public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
@@ -61,6 +62,7 @@ public class TransitionInfoBuilder {
}
public TransitionInfoBuilder addChange(TransitionInfo.Change change) {
+ change.setDisplayId(DISPLAY_ID, DISPLAY_ID);
mInfo.addChange(change);
return this;
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 6946e6bf88a8..03e1e66a3cac 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -59,12 +59,14 @@ class RemoteTransitionAdapter {
// changes should be ordered top-to-bottom in z
val mode = change.mode
+ var rootIdx = info.findRootIndex(change.endDisplayId)
+ if (rootIdx < 0) rootIdx = 0
// Launcher animates leaf tasks directly, so always reparent all task leashes to root.
- t.reparent(leash, info.rootLeash)
+ t.reparent(leash, info.getRoot(rootIdx).leash)
t.setPosition(
leash,
- (change.startAbsBounds.left - info.rootOffset.x).toFloat(),
- (change.startAbsBounds.top - info.rootOffset.y).toFloat()
+ (change.startAbsBounds.left - info.getRoot(rootIdx).offset.x).toFloat(),
+ (change.startAbsBounds.top - info.getRoot(rootIdx).offset.y).toFloat()
)
t.show(leash)
// Put all the OPEN/SHOW on top
@@ -114,8 +116,11 @@ class RemoteTransitionAdapter {
.setName(change.leash.toString() + "_transition-leash")
.setContainerLayer()
.setParent(
- if (change.parent == null) info.rootLeash
- else info.getChange(change.parent!!)!!.leash
+ if (change.parent == null) {
+ var rootIdx = info.findRootIndex(change.endDisplayId)
+ if (rootIdx < 0) rootIdx = 0
+ info.getRoot(rootIdx).leash
+ } else info.getChange(change.parent!!)!!.leash
)
.build()
// Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 6f7d66d03cab..58e7747a7a9f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -321,7 +321,9 @@ public class RemoteTransitionCompat {
} else {
// We are receiving new opening tasks, so convert to onTasksAppeared.
targets[i] = TransitionUtil.newTarget(change, layer, info, t, mLeashMap);
- t.reparent(targets[i].leash, mInfo.getRootLeash());
+ // reparent into the original `mInfo` since that's where we are animating.
+ final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+ t.reparent(targets[i].leash, mInfo.getRoot(rootIdx).getLeash());
t.setLayer(targets[i].leash, layer);
mOpeningTasks.add(new TaskState(change, targets[i].leash));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
index 64e58d0de2b9..0e2a3acd3df4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
@@ -134,7 +134,7 @@ public class RemoteTransitionTest extends SysuiTestCase {
TransitionInfoBuilder(@WindowManager.TransitionType int type) {
mInfo = new TransitionInfo(type, 0 /* flags */);
- mInfo.setRootLeash(createMockSurface(true /* valid */), 0, 0);
+ mInfo.addRootLeash(0, createMockSurface(true /* valid */), 0, 0);
}
TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
@@ -144,6 +144,7 @@ public class RemoteTransitionTest extends SysuiTestCase {
change.setMode(mode);
change.setFlags(flags);
change.setTaskInfo(taskInfo);
+ change.setDisplayId(0, 0);
mInfo.addChange(change);
return this;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 606f011ae55c..4e0f120759d9 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -675,7 +675,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
* needs to be passed/applied in shell because until finish is called, shell owns the surfaces.
* Additionally, this gives shell the ability to better deal with merged transitions.
*/
- private void buildFinishTransaction(SurfaceControl.Transaction t, SurfaceControl rootLeash) {
+ private void buildFinishTransaction(SurfaceControl.Transaction t, TransitionInfo info) {
final Point tmpPos = new Point();
// usually only size 1
final ArraySet<DisplayContent> displays = new ArraySet<>();
@@ -728,8 +728,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
} finally {
mController.mBuildingFinishLayers = false;
}
- if (rootLeash.isValid()) {
- t.reparent(rootLeash, null);
+ for (int i = 0; i < info.getRootCount(); ++i) {
+ t.reparent(info.getRoot(i).getLeash(), null);
}
}
@@ -1066,13 +1066,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
return;
}
- if (mTargetDisplays.isEmpty()) {
- mTargetDisplays.add(mController.mAtm.mRootWindowContainer.getDefaultDisplay());
- }
- // While there can be multiple DC's involved. For now, we just use the first one as
- // the "primary" one for most things. Eventually, this will need to change, but, for the
- // time being, we don't have full cross-display transitions so it isn't a problem.
- final DisplayContent dc = mTargetDisplays.get(0);
// Commit the visibility of visible activities before calculateTransitionInfo(), so the
// TaskInfo can be visible. Also it needs to be done before moveToPlaying(), otherwise
@@ -1082,6 +1075,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (mState == STATE_ABORT) {
mController.abort(this);
+ // Fall-back to the default display if there isn't one participating.
+ final DisplayContent dc = !mTargetDisplays.isEmpty() ? mTargetDisplays.get(0)
+ : mController.mAtm.mRootWindowContainer.getDefaultDisplay();
dc.getPendingTransaction().merge(transaction);
mSyncId = -1;
mOverrideOptions = null;
@@ -1094,16 +1090,24 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
mController.moveToPlaying(this);
- if (dc.isKeyguardLocked()) {
- mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
- }
-
// Check whether the participants were animated from back navigation.
final boolean markBackAnimated = mController.mAtm.mBackNavigationController
.containsBackAnimationTargets(this);
- // Resolve the animating targets from the participants
+ // Resolve the animating targets from the participants.
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
+
+ // Repopulate the displays based on the resolved targets.
+ mTargetDisplays.clear();
+ for (int i = 0; i < info.getRootCount(); ++i) {
+ final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent(
+ info.getRoot(i).getDisplayId());
+ mTargetDisplays.add(dc);
+ if (dc.isKeyguardLocked()) {
+ mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
+ }
+ }
+
if (markBackAnimated) {
mController.mAtm.mBackNavigationController.clearBackAnimations(mStartTransaction);
}
@@ -1125,9 +1129,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
// TODO(b/188669821): Move to animation impl in shell.
- handleLegacyRecentsStartBehavior(dc, info);
+ for (int i = 0; i < mTargetDisplays.size(); ++i) {
+ handleLegacyRecentsStartBehavior(mTargetDisplays.get(i), info);
+ if (mRecentsDisplayId != INVALID_DISPLAY) break;
+ }
- handleNonAppWindowsInTransition(dc, mType, mFlags);
+ handleNonAppWindowsInTransition(mType, mFlags);
// The callback is only populated for custom activity-level client animations
sendRemoteCallback(mClientAnimationStartCallback);
@@ -1191,11 +1198,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// This is non-null only if display has changes. It handles the visible windows that don't
// need to be participated in the transition.
- final AsyncRotationController controller = dc.getAsyncRotationController();
- if (controller != null && containsChangeFor(dc, mTargets)) {
- controller.setupStartTransaction(transaction);
+ for (int i = 0; i < mTargetDisplays.size(); ++i) {
+ final DisplayContent dc = mTargetDisplays.get(i);
+ final AsyncRotationController controller = dc.getAsyncRotationController();
+ if (controller != null && containsChangeFor(dc, mTargets)) {
+ controller.setupStartTransaction(transaction);
+ }
}
- buildFinishTransaction(mFinishTransaction, info.getRootLeash());
+ buildFinishTransaction(mFinishTransaction, info);
if (mController.getTransitionPlayer() != null && mIsPlayerEnabled) {
mController.dispatchLegacyAppTransitionStarting(info, mStatusBarTransitionDelay);
try {
@@ -1216,10 +1226,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// client, we should finish and apply it here so the transactions aren't lost.
postCleanupOnFailure();
}
- final AccessibilityController accessibilityController =
- dc.mWmService.mAccessibilityController;
- if (accessibilityController.hasCallbacks()) {
- accessibilityController.onWMTransition(dc.getDisplayId(), mType);
+ for (int i = 0; i < mTargetDisplays.size(); ++i) {
+ final DisplayContent dc = mTargetDisplays.get(i);
+ final AccessibilityController accessibilityController =
+ dc.mWmService.mAccessibilityController;
+ if (accessibilityController.hasCallbacks()) {
+ accessibilityController.onWMTransition(dc.getDisplayId(), mType);
+ }
}
} else {
// No player registered or it's not enabled, so just finish/apply immediately
@@ -1296,16 +1309,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) {
return;
}
- mRecentsDisplayId = dc.mDisplayId;
// Recents has an input-consumer to grab input from the "live tile" app. Set that up here
final InputConsumerImpl recentsAnimationInputConsumer =
dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ ActivityRecord recentsActivity = null;
if (recentsAnimationInputConsumer != null) {
// find the top-most going-away activity and the recents activity. The top-most
// is used as layer reference while the recents is used for registering the consumer
// override.
- ActivityRecord recentsActivity = null;
ActivityRecord topActivity = null;
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -1329,6 +1341,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
}
+ if (recentsActivity == null) {
+ // No recents activity on `dc`, its probably on a different display.
+ return;
+ }
+ mRecentsDisplayId = dc.mDisplayId;
+
// The rest of this function handles nav-bar reparenting
if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
@@ -1423,7 +1441,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
}
- private void handleNonAppWindowsInTransition(@NonNull DisplayContent dc,
+ private void handleNonAppWindowsInTransition(
@TransitionType int transit, @TransitionFlags int flags) {
if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
// If the occlusion changed but the transition isn't an occlude/unocclude transition,
@@ -1801,6 +1819,41 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return wc instanceof DisplayContent;
}
+ private static int getDisplayId(@NonNull WindowContainer wc) {
+ return wc.getDisplayContent() != null
+ ? wc.getDisplayContent().getDisplayId() : INVALID_DISPLAY;
+ }
+
+ @VisibleForTesting
+ static void calculateTransitionRoots(@NonNull TransitionInfo outInfo,
+ ArrayList<ChangeInfo> sortedTargets,
+ @NonNull SurfaceControl.Transaction startT) {
+ // There needs to be a root on each display.
+ for (int i = 0; i < sortedTargets.size(); ++i) {
+ final WindowContainer<?> wc = sortedTargets.get(i).mContainer;
+ // Don't include wallpapers since they are in a different DA.
+ if (isWallpaper(wc)) continue;
+ final int endDisplayId = getDisplayId(wc);
+ if (endDisplayId < 0) continue;
+
+ // Check if Root was already created for this display with a higher-Z window
+ if (outInfo.findRootIndex(endDisplayId) >= 0) continue;
+
+ WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, wc);
+
+ // Make leash based on highest (z-order) direct child of ancestor with a participant.
+ WindowContainer leashReference = wc;
+ while (leashReference.getParent() != ancestor) {
+ leashReference = leashReference.getParent();
+ }
+ final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
+ "Transition Root: " + leashReference.getName()).build();
+ startT.setLayer(rootLeash, leashReference.getLastLayer());
+ outInfo.addRootLeash(endDisplayId, rootLeash,
+ ancestor.getBounds().left, ancestor.getBounds().top);
+ }
+ }
+
/**
* Construct a TransitionInfo object from a set of targets and changes. Also populates the
* root surface.
@@ -1811,37 +1864,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
@NonNull
static TransitionInfo calculateTransitionInfo(@TransitionType int type, int flags,
ArrayList<ChangeInfo> sortedTargets,
- @Nullable SurfaceControl.Transaction startT) {
+ @NonNull SurfaceControl.Transaction startT) {
final TransitionInfo out = new TransitionInfo(type, flags);
-
- WindowContainer<?> topApp = null;
- for (int i = 0; i < sortedTargets.size(); i++) {
- final WindowContainer<?> wc = sortedTargets.get(i).mContainer;
- if (!isWallpaper(wc)) {
- topApp = wc;
- break;
- }
- }
- if (topApp == null) {
- out.setRootLeash(new SurfaceControl(), 0, 0);
+ calculateTransitionRoots(out, sortedTargets, startT);
+ if (out.getRootCount() == 0) {
return out;
}
- WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, topApp);
-
- // Make leash based on highest (z-order) direct child of ancestor with a participant.
- // TODO(b/261418859): Handle the case when the target contains window containers which
- // belong to a different display. As a workaround we use topApp, from which wallpaper
- // window container is removed, instead of sortedTargets here.
- WindowContainer leashReference = topApp;
- while (leashReference.getParent() != ancestor) {
- leashReference = leashReference.getParent();
- }
- final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
- "Transition Root: " + leashReference.getName()).build();
- startT.setLayer(rootLeash, leashReference.getLastLayer());
- out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top);
-
// Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
final int count = sortedTargets.size();
for (int i = 0; i < count; ++i) {
@@ -1861,6 +1890,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
change.setMode(info.getTransitMode(target));
change.setStartAbsBounds(info.mAbsoluteBounds);
change.setFlags(info.getChangeFlags(target));
+ change.setDisplayId(info.mDisplayId, getDisplayId(target));
final Task task = target.asTask();
final TaskFragment taskFragment = target.asTaskFragment();
@@ -1947,6 +1977,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
TransitionInfo.AnimationOptions animOptions = null;
+
+ // Check if the top-most app is an activity (ie. activity->activity). If so, make sure to
+ // honor its custom transition options.
+ WindowContainer<?> topApp = null;
+ for (int i = 0; i < sortedTargets.size(); i++) {
+ if (isWallpaper(sortedTargets.get(i).mContainer)) continue;
+ topApp = sortedTargets.get(i).mContainer;
+ break;
+ }
if (topApp.asActivityRecord() != null) {
final ActivityRecord topActivity = topApp.asActivityRecord();
animOptions = addCustomActivityTransition(topActivity, true/* open */, null);
@@ -1997,14 +2036,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
private static WindowContainer<?> findCommonAncestor(
@NonNull ArrayList<ChangeInfo> targets,
@NonNull WindowContainer<?> topApp) {
+ final int displayId = getDisplayId(topApp);
WindowContainer<?> ancestor = topApp.getParent();
// Go up ancestor parent chain until all targets are descendants. Ancestor should never be
// null because all targets are attached.
for (int i = targets.size() - 1; i >= 0; i--) {
final ChangeInfo change = targets.get(i);
final WindowContainer wc = change.mContainer;
- if (isWallpaper(wc)) {
- // Skip the non-app window.
+ if (isWallpaper(wc) || getDisplayId(wc) != displayId) {
+ // Skip the non-app window or windows on a different display
continue;
}
while (!wc.isDescendantOf(ancestor)) {
@@ -2180,6 +2220,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final Rect mAbsoluteBounds = new Rect();
boolean mShowWallpaper;
int mRotation = ROTATION_UNDEFINED;
+ int mDisplayId = -1;
@ActivityInfo.Config int mKnownConfigChanges;
/** These are just extra info. They aren't used for change-detection. */
@@ -2197,6 +2238,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mShowWallpaper = origState.showWallpaper();
mRotation = origState.getWindowConfiguration().getRotation();
mStartParent = origState.getParent();
+ mDisplayId = getDisplayId(origState);
}
@VisibleForTesting
@@ -2228,7 +2270,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// assume no change in windowing-mode.
|| (mWindowingMode != 0 && mContainer.getWindowingMode() != mWindowingMode)
|| !mContainer.getBounds().equals(mAbsoluteBounds)
- || mRotation != mContainer.getWindowConfiguration().getRotation();
+ || mRotation != mContainer.getWindowConfiguration().getRotation()
+ || mDisplayId != getDisplayId(mContainer);
}
@TransitionInfo.TransitionMode
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index debfc841e52d..304581297932 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -520,6 +520,47 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testCreateInfo_MultiDisplay() {
+ DisplayContent otherDisplay = createNewDisplay();
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task display0Task = createTask(mDisplayContent);
+ final Task display1Task = createTask(otherDisplay);
+ // Start states.
+ changes.put(display0Task,
+ new Transition.ChangeInfo(display0Task, false /* vis */, true /* exChg */));
+ changes.put(display1Task,
+ new Transition.ChangeInfo(display1Task, false /* vis */, true /* exChg */));
+ fillChangeMap(changes, display0Task);
+ fillChangeMap(changes, display1Task);
+ // End states.
+ display0Task.setVisibleRequested(true);
+ display1Task.setVisibleRequested(true);
+
+ final int transit = transition.mType;
+ int flags = 0;
+
+ participants.add(display0Task);
+ participants.add(display1Task);
+ ArrayList<Transition.ChangeInfo> targets =
+ Transition.calculateTargets(participants, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
+ assertEquals(2, info.getRootCount());
+ // Check that the changes are assigned to the correct display
+ assertEquals(mDisplayContent.getDisplayId(), info.getChange(
+ display0Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId());
+ assertEquals(otherDisplay.getDisplayId(), info.getChange(
+ display1Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId());
+ // Check that roots can be found by display and have the correct display
+ assertEquals(mDisplayContent.getDisplayId(),
+ info.getRoot(info.findRootIndex(mDisplayContent.getDisplayId())).getDisplayId());
+ assertEquals(otherDisplay.getDisplayId(),
+ info.getRoot(info.findRootIndex(otherDisplay.getDisplayId())).getDisplayId());
+ }
+
+ @Test
public void testTargets_noIntermediatesToWallpaper() {
final Transition transition = createTestTransition(TRANSIT_OPEN);