Loading core/java/android/window/TransitionInfo.java +69 −19 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.window; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; Loading Loading @@ -66,6 +67,9 @@ public final class TransitionInfo implements Parcelable { private final @WindowManager.TransitionOldType int mType; private final ArrayList<Change> mChanges = new ArrayList<>(); private SurfaceControl mRootLeash; private final Point mRootOffset = new Point(); /** @hide */ public TransitionInfo(@WindowManager.TransitionOldType int type) { mType = type; Loading @@ -74,6 +78,9 @@ public final class TransitionInfo implements Parcelable { private TransitionInfo(Parcel in) { mType = in.readInt(); in.readList(mChanges, null /* classLoader */); mRootLeash = new SurfaceControl(); mRootLeash.readFromParcel(in); mRootOffset.readFromParcel(in); } @Override Loading @@ -81,6 +88,8 @@ public final class TransitionInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); dest.writeList(mChanges); mRootLeash.writeToParcel(dest, flags); mRootOffset.writeToParcel(dest, flags); } @NonNull Loading @@ -103,10 +112,35 @@ 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); } public int getType() { return mType; } /** * @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. */ @NonNull public SurfaceControl getRootLeash() { if (mRootLeash == null) { throw new IllegalStateException("Trying to get a leash which wasn't set"); } return mRootLeash; } /** @return the offset (relative to the screen) of the root leash. */ @NonNull public Point getRootOffset() { return mRootOffset; } @NonNull public List<Change> getChanges() { return mChanges; Loading Loading @@ -136,7 +170,7 @@ public final class TransitionInfo implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{t=" + mType + " c=["); sb.append("{t=" + mType + " ro=" + mRootOffset + " c=["); for (int i = 0; i < mChanges.size(); ++i) { if (i > 0) { sb.append(','); Loading Loading @@ -167,8 +201,9 @@ public final class TransitionInfo implements Parcelable { private WindowContainerToken mParent; private final SurfaceControl mLeash; private int mMode = TRANSIT_NONE; private final Rect mStartBounds = new Rect(); private final Rect mEndBounds = new Rect(); private final Rect mStartAbsBounds = new Rect(); private final Rect mEndAbsBounds = new Rect(); private final Point mEndRelOffset = new Point(); public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { mContainer = container; Loading @@ -181,8 +216,9 @@ public final class TransitionInfo implements Parcelable { mLeash = new SurfaceControl(); mLeash.readFromParcel(in); mMode = in.readInt(); mStartBounds.readFromParcel(in); mEndBounds.readFromParcel(in); mStartAbsBounds.readFromParcel(in); mEndAbsBounds.readFromParcel(in); mEndRelOffset.readFromParcel(in); } /** Sets the parent of this change's container. The parent must be a participant or null. */ Loading @@ -195,14 +231,19 @@ public final class TransitionInfo implements Parcelable { mMode = mode; } /** Sets the bounds this container occupied before the change */ public void setStartBounds(@Nullable Rect rect) { mStartBounds.set(rect); /** Sets the bounds this container occupied before the change in screen space */ public void setStartAbsBounds(@Nullable Rect rect) { mStartAbsBounds.set(rect); } /** Sets the bounds this container will occupy after the change */ public void setEndBounds(@Nullable Rect rect) { mEndBounds.set(rect); /** Sets the bounds this container will occupy after the change in screen space */ public void setEndAbsBounds(@Nullable Rect rect) { mEndAbsBounds.set(rect); } /** Sets the offset of this container from its parent surface */ public void setEndRelOffset(int left, int top) { mEndRelOffset.set(left, top); } /** @return the container that is changing. May be null if non-remotable (eg. activity) */ Loading Loading @@ -230,8 +271,8 @@ public final class TransitionInfo implements Parcelable { * is coming into existence. */ @NonNull public Rect getStartBounds() { return mStartBounds; public Rect getStartAbsBounds() { return mStartAbsBounds; } /** Loading @@ -239,8 +280,16 @@ public final class TransitionInfo implements Parcelable { * is disappearing. */ @NonNull public Rect getEndBounds() { return mEndBounds; public Rect getEndAbsBounds() { return mEndAbsBounds; } /** * @return the offset of the container's surface from its parent surface after the change. */ @NonNull public Point getEndRelOffset() { return mEndRelOffset; } /** @return the leash or surface to animate for this container */ Loading @@ -256,8 +305,9 @@ public final class TransitionInfo implements Parcelable { dest.writeTypedObject(mParent, flags); mLeash.writeToParcel(dest, flags); dest.writeInt(mMode); mStartBounds.writeToParcel(dest, flags); mEndBounds.writeToParcel(dest, flags); mStartAbsBounds.writeToParcel(dest, flags); mEndAbsBounds.writeToParcel(dest, flags); mEndRelOffset.writeToParcel(dest, flags); } @NonNull Loading @@ -283,8 +333,8 @@ public final class TransitionInfo implements Parcelable { @Override public String toString() { return "{" + mContainer + "(" + mParent + ") leash=" + mLeash + " m=" + modeToString(mMode) + " sb=" + mStartBounds + " eb=" + mEndBounds + "}"; + " m=" + modeToString(mMode) + " sb=" + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}"; } } } libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +36 −5 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ public class Transitions extends ITransitionPlayer.Stub { } @Override public void onTransitionReady(@NonNull IBinder transitionToken, TransitionInfo info, public void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); Loading @@ -131,22 +131,53 @@ public class Transitions extends ITransitionPlayer.Stub { + transitionToken); } mActiveTransitions.put(transitionToken, new ArrayList<>()); for (int i = 0; i < info.getChanges().size(); ++i) { final SurfaceControl leash = info.getChanges().get(i).getLeash(); boolean isOpening = isOpeningType(info.getType()); if (info.getRootLeash().isValid()) { t.show(info.getRootLeash()); } // changes should be ordered top-to-bottom in z for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); final SurfaceControl leash = change.getLeash(); final int mode = info.getChanges().get(i).getMode(); // Don't animate anything with an animating parent if (change.getParent() != null) { if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); if (isOpeningType(info.getType())) { } continue; } t.reparent(leash, info.getRootLeash()); t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, change.getEndAbsBounds().top - info.getRootOffset().y); // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); if (isOpening) { // put on top and fade in t.setLayer(leash, info.getChanges().size() - i); t.setAlpha(leash, 0.f); startExampleAnimation(transitionToken, leash, true /* show */); } else { // put on bottom and leave it visible without fade t.setLayer(leash, -i); t.setAlpha(leash, 1.f); } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) { if (!isOpeningType(info.getType())) { if (isOpening) { // put on bottom and leave visible without fade t.setLayer(leash, -i); } else { // put on top and fade out t.setLayer(leash, info.getChanges().size() - i); startExampleAnimation(transitionToken, leash, false /* show */); } } else { t.setLayer(leash, info.getChanges().size() - i); } } t.apply(); Loading services/core/java/com/android/server/wm/Transition.java +146 −23 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import android.annotation.IntDef; import android.annotation.NonNull; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; Loading @@ -46,7 +47,6 @@ import com.android.internal.protolog.common.ProtoLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Set; /** * Represents a logical transition. Loading Loading @@ -90,6 +90,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private final TransitionController mController; private final BLASTSyncEngine mSyncEngine; /** * This is a leash to put animating surfaces into flatly without clipping/ordering issues. It * is a child of all the targets' shared ancestor. */ private SurfaceControl mRootLeash = null; /** * Contains change infos for both participants and all ancestors. We have to track ancestors * because they are all promotion candidates and thus we need their start-states Loading @@ -97,7 +103,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe */ final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>(); /** The collected participants in the transition. */ final ArraySet<WindowContainer> mParticipants = new ArraySet<>(); /** The final animation targets derived from participants after promotion. */ private ArraySet<WindowContainer> mTargets = null; private @TransitionState int mState = STATE_COLLECTING; private boolean mReadyCalled = false; Loading Loading @@ -190,6 +201,42 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (mState < STATE_PLAYING) { throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId); } final Point tmpPos = new Point(); // usually only size 1 final ArraySet<DisplayContent> displays = new ArraySet<>(); // Immediately apply all surface reparents, don't wait for pending/sync/etc. SurfaceControl.Transaction t = mController.mAtm.mWindowManager.mTransactionFactory.get(); for (int i = mTargets.size() - 1; i >= 0; --i) { final WindowContainer target = mTargets.valueAt(i); if (target.getParent() != null) { // Ensure surfaceControls are re-parented back into the hierarchy. t.reparent(target.getSurfaceControl(), target.getParent().getSurfaceControl()); target.getRelativePosition(tmpPos); t.setPosition(target.getSurfaceControl(), tmpPos.x, tmpPos.y); displays.add(target.getDisplayContent()); } } // Need to update layers on ALL displays (for now) since they were all paused while // the animation played. for (int i = displays.size() - 1; i >= 0; --i) { if (displays.valueAt(i) == null) continue; displays.valueAt(i).assignChildLayers(t); } // Also pro-actively hide going-invisible activity surfaces in same transaction to // prevent flickers due to reparenting and animation z-order mismatch. for (int i = mParticipants.size() - 1; i >= 0; --i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar == null || ar.mVisibleRequested || !ar.isVisible()) continue; t.hide(ar.getSurfaceControl()); } if (mRootLeash.isValid()) { t.remove(mRootLeash); } mRootLeash = null; t.apply(); t.close(); // Commit all going-invisible containers for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar == null || ar.mVisibleRequested) { Loading Loading @@ -234,7 +281,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mState = STATE_PLAYING; mController.moveToPlaying(this); final TransitionInfo info = calculateTransitionInfo(mType, mParticipants, mChanges); // Resolve the animating targets from the participants mTargets = calculateTargets(mParticipants, mChanges); final TransitionInfo info = calculateTransitionInfo(mType, mTargets, mChanges); mRootLeash = info.getRootLeash(); handleNonAppWindowsInTransition(displayId, mType, mFlags); Loading @@ -245,11 +296,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mController.getTransitionPlayer().onTransitionReady(this, info, transaction); } catch (RemoteException e) { // If there's an exception when trying to send the mergedTransaction to the // client, we should immediately apply it here so the transactions aren't lost. // client, we should finish and apply it here so the transactions aren't lost. transaction.apply(); finishTransition(); } } else { // No player registered, so just finish/apply immediately transaction.apply(); finishTransition(); } mSyncId = -1; } Loading Loading @@ -325,10 +379,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * * @return {@code true} if transition in target can be promoted to its parent. */ private static boolean canPromote( WindowContainer target, ArraySet<WindowContainer> topTargets) { private static boolean canPromote(WindowContainer target, ArraySet<WindowContainer> topTargets, ArrayMap<WindowContainer, ChangeInfo> changes) { final WindowContainer parent = target.getParent(); if (parent == null || !parent.canCreateRemoteAnimationTarget()) { final ChangeInfo parentChanges = parent != null ? changes.get(parent) : null; if (parent == null || !parent.canCreateRemoteAnimationTarget() || parentChanges == null || !parentChanges.hasChanged(parent)) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " SKIP: %s", parent == null ? "no parent" : ("parent can't be target " + parent)); return false; Loading Loading @@ -394,14 +450,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Go through each target until we find one that can be promoted. for (WindowContainer targ : topTargets) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " checking %s", targ); if (!canPromote(targ, topTargets)) { if (!canPromote(targ, topTargets, changes)) { continue; } final WindowContainer parent = targ.getParent(); // No obstructions found to promotion, so promote final WindowContainer parent = targ.getParent(); final ChangeInfo parentInfo = changes.get(parent); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " CAN PROMOTE: promoting to parent %s", parent); final ChangeInfo parentInfo = changes.get(parent); targets.add(parent); // Go through all children of newly-promoted container and remove them from the Loading Loading @@ -443,10 +499,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * animation targets to higher level in the window hierarchy if possible. */ @VisibleForTesting static TransitionInfo calculateTransitionInfo(int type, Set<WindowContainer> participants, @NonNull static ArraySet<WindowContainer> calculateTargets(ArraySet<WindowContainer> participants, ArrayMap<WindowContainer, ChangeInfo> changes) { final TransitionInfo out = new TransitionInfo(type); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start calculating TransitionInfo based on participants: %s", participants); Loading @@ -470,6 +525,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Search through ancestors to find the top-most participant (if one exists) WindowContainer topParent = null; tmpList.clear(); if (reportIfNotTop(wc)) { tmpList.add(wc); } for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) { if (participants.contains(p)) { topParent = p; Loading @@ -479,8 +537,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } if (topParent != null) { // There was an ancestor participant, so don't add wc to targets. However, continue // to add any always-report parents along the way. // There was an ancestor participant, so don't add wc to targets unless always- // report. Similarly, add any always-report parents along the way. for (int i = 0; i < tmpList.size(); ++i) { targets.add(tmpList.get(i)); final ChangeInfo info = changes.get(tmpList.get(i)); Loading Loading @@ -508,10 +566,70 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe while (tryPromote(topTargets, targets, changes)) { // Empty on purpose } return targets; } // Convert all the resolved ChangeInfos into a TransactionInfo object. for (int i = targets.size() - 1; i >= 0; --i) { final WindowContainer target = targets.valueAt(i); /** Add any of `members` within `root` to `out` in top-to-bottom z-order. */ private static void addMembersInOrder(WindowContainer root, ArraySet<WindowContainer> members, ArrayList<WindowContainer> out) { for (int i = root.getChildCount() - 1; i >= 0; --i) { final WindowContainer child = root.getChildAt(i); addMembersInOrder(child, members, out); if (members.contains(child)) { out.add(child); } } } /** * Construct a TransitionInfo object from a set of targets and changes. Also populates the * root surface. */ @VisibleForTesting @NonNull static TransitionInfo calculateTransitionInfo(int type, ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) { final TransitionInfo out = new TransitionInfo(type); if (targets.isEmpty()) { out.setRootLeash(new SurfaceControl(), 0, 0); return out; } // Find the top-most shared ancestor WindowContainer ancestor = targets.valueAt(0).getParent(); // Go up ancestor parent chain until all topTargets are descendants. ancestorLoop: while (ancestor != null) { for (int i = 1; i < targets.size(); ++i) { if (!targets.valueAt(i).isDescendantOf(ancestor)) { ancestor = ancestor.getParent(); continue ancestorLoop; } } break; } // Sort targets top-to-bottom in Z. ArrayList<WindowContainer> sortedTargets = new ArrayList<>(); addMembersInOrder(ancestor, targets, sortedTargets); // make leash based on highest (z-order) direct child of ancestor with a participant. WindowContainer leashReference = sortedTargets.get(0); while (leashReference.getParent() != ancestor) { leashReference = leashReference.getParent(); } final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName( "Transition Root: " + leashReference.getName()).build(); SurfaceControl.Transaction t = ancestor.mWmService.mTransactionFactory.get(); t.setLayer(rootLeash, leashReference.getLastLayer()); t.apply(); t.close(); 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) { final WindowContainer target = sortedTargets.get(i); final ChangeInfo info = changes.get(target); final TransitionInfo.Change change = new TransitionInfo.Change( target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken() Loading @@ -520,8 +638,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe change.setParent(info.mParent.mRemoteToken.toWindowContainerToken()); } change.setMode(info.getTransitMode(target)); change.setStartBounds(info.mAbsoluteBounds); change.setEndBounds(target.getBounds()); change.setStartAbsBounds(info.mAbsoluteBounds); change.setEndAbsBounds(target.getBounds()); change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left, target.getBounds().top - target.getParent().getBounds().top); out.addChange(change); } Loading @@ -543,23 +663,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // before change state boolean mVisible; int mWindowingMode; Rect mAbsoluteBounds; final Rect mAbsoluteBounds = new Rect(); ChangeInfo(@NonNull WindowContainer origState) { mVisible = origState.isVisibleRequested(); mWindowingMode = origState.getWindowingMode(); mAbsoluteBounds = origState.getBounds(); mAbsoluteBounds.set(origState.getBounds()); } @VisibleForTesting ChangeInfo(boolean visible, boolean existChange) { mVisible = visible; mAbsoluteBounds = new Rect(); mExistenceChanged = existChange; } boolean hasChanged(@NonNull WindowContainer newState) { return newState.isVisibleRequested() != mVisible // If it's invisible and hasn't changed visibility, always return false since even if // something changed, it wouldn't be a visible change. final boolean currVisible = newState.isVisibleRequested(); if (currVisible == mVisible && !mVisible) return false; return currVisible != mVisible // if mWindowingMode is 0, this container wasn't attached at collect time, so // assume no change in windowing-mode. || (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode) Loading services/core/java/com/android/server/wm/TransitionController.java +9 −1 Original line number Diff line number Diff line Loading @@ -109,10 +109,18 @@ class TransitionController { return mCollectingTransition != null; } /** * @return {@code true} if transition is actively playing. This is not necessarily {@code true} * during collection. */ boolean isPlaying() { return !mPlayingTransitions.isEmpty(); } /** @return {@code true} if a transition is running */ boolean inTransition() { // TODO(shell-transitions): eventually properly support multiple return mCollectingTransition != null || !mPlayingTransitions.isEmpty(); return isCollecting() || isPlaying(); } /** @return {@code true} if wc is in a participant subtree */ Loading services/core/java/com/android/server/wm/WindowContainer.java +7 −0 Original line number Diff line number Diff line Loading @@ -2089,6 +2089,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } void assignLayer(Transaction t, int layer) { // Don't assign layers while a transition animation is playing // TODO(b/173528115): establish robust best-practices around z-order fighting. if (mWmService.mAtmService.getTransitionController().isPlaying()) return; final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; if (mSurfaceControl != null && changed) { setLayer(t, layer); Loading @@ -2113,6 +2116,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSurfaceAnimator.setLayer(t, layer); } int getLastLayer() { return mLastLayer; } protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) { // Route through surface animator to accommodate that our surface control might be Loading Loading
core/java/android/window/TransitionInfo.java +69 −19 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.window; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; Loading Loading @@ -66,6 +67,9 @@ public final class TransitionInfo implements Parcelable { private final @WindowManager.TransitionOldType int mType; private final ArrayList<Change> mChanges = new ArrayList<>(); private SurfaceControl mRootLeash; private final Point mRootOffset = new Point(); /** @hide */ public TransitionInfo(@WindowManager.TransitionOldType int type) { mType = type; Loading @@ -74,6 +78,9 @@ public final class TransitionInfo implements Parcelable { private TransitionInfo(Parcel in) { mType = in.readInt(); in.readList(mChanges, null /* classLoader */); mRootLeash = new SurfaceControl(); mRootLeash.readFromParcel(in); mRootOffset.readFromParcel(in); } @Override Loading @@ -81,6 +88,8 @@ public final class TransitionInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); dest.writeList(mChanges); mRootLeash.writeToParcel(dest, flags); mRootOffset.writeToParcel(dest, flags); } @NonNull Loading @@ -103,10 +112,35 @@ 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); } public int getType() { return mType; } /** * @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. */ @NonNull public SurfaceControl getRootLeash() { if (mRootLeash == null) { throw new IllegalStateException("Trying to get a leash which wasn't set"); } return mRootLeash; } /** @return the offset (relative to the screen) of the root leash. */ @NonNull public Point getRootOffset() { return mRootOffset; } @NonNull public List<Change> getChanges() { return mChanges; Loading Loading @@ -136,7 +170,7 @@ public final class TransitionInfo implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{t=" + mType + " c=["); sb.append("{t=" + mType + " ro=" + mRootOffset + " c=["); for (int i = 0; i < mChanges.size(); ++i) { if (i > 0) { sb.append(','); Loading Loading @@ -167,8 +201,9 @@ public final class TransitionInfo implements Parcelable { private WindowContainerToken mParent; private final SurfaceControl mLeash; private int mMode = TRANSIT_NONE; private final Rect mStartBounds = new Rect(); private final Rect mEndBounds = new Rect(); private final Rect mStartAbsBounds = new Rect(); private final Rect mEndAbsBounds = new Rect(); private final Point mEndRelOffset = new Point(); public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { mContainer = container; Loading @@ -181,8 +216,9 @@ public final class TransitionInfo implements Parcelable { mLeash = new SurfaceControl(); mLeash.readFromParcel(in); mMode = in.readInt(); mStartBounds.readFromParcel(in); mEndBounds.readFromParcel(in); mStartAbsBounds.readFromParcel(in); mEndAbsBounds.readFromParcel(in); mEndRelOffset.readFromParcel(in); } /** Sets the parent of this change's container. The parent must be a participant or null. */ Loading @@ -195,14 +231,19 @@ public final class TransitionInfo implements Parcelable { mMode = mode; } /** Sets the bounds this container occupied before the change */ public void setStartBounds(@Nullable Rect rect) { mStartBounds.set(rect); /** Sets the bounds this container occupied before the change in screen space */ public void setStartAbsBounds(@Nullable Rect rect) { mStartAbsBounds.set(rect); } /** Sets the bounds this container will occupy after the change */ public void setEndBounds(@Nullable Rect rect) { mEndBounds.set(rect); /** Sets the bounds this container will occupy after the change in screen space */ public void setEndAbsBounds(@Nullable Rect rect) { mEndAbsBounds.set(rect); } /** Sets the offset of this container from its parent surface */ public void setEndRelOffset(int left, int top) { mEndRelOffset.set(left, top); } /** @return the container that is changing. May be null if non-remotable (eg. activity) */ Loading Loading @@ -230,8 +271,8 @@ public final class TransitionInfo implements Parcelable { * is coming into existence. */ @NonNull public Rect getStartBounds() { return mStartBounds; public Rect getStartAbsBounds() { return mStartAbsBounds; } /** Loading @@ -239,8 +280,16 @@ public final class TransitionInfo implements Parcelable { * is disappearing. */ @NonNull public Rect getEndBounds() { return mEndBounds; public Rect getEndAbsBounds() { return mEndAbsBounds; } /** * @return the offset of the container's surface from its parent surface after the change. */ @NonNull public Point getEndRelOffset() { return mEndRelOffset; } /** @return the leash or surface to animate for this container */ Loading @@ -256,8 +305,9 @@ public final class TransitionInfo implements Parcelable { dest.writeTypedObject(mParent, flags); mLeash.writeToParcel(dest, flags); dest.writeInt(mMode); mStartBounds.writeToParcel(dest, flags); mEndBounds.writeToParcel(dest, flags); mStartAbsBounds.writeToParcel(dest, flags); mEndAbsBounds.writeToParcel(dest, flags); mEndRelOffset.writeToParcel(dest, flags); } @NonNull Loading @@ -283,8 +333,8 @@ public final class TransitionInfo implements Parcelable { @Override public String toString() { return "{" + mContainer + "(" + mParent + ") leash=" + mLeash + " m=" + modeToString(mMode) + " sb=" + mStartBounds + " eb=" + mEndBounds + "}"; + " m=" + modeToString(mMode) + " sb=" + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}"; } } }
libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +36 −5 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ public class Transitions extends ITransitionPlayer.Stub { } @Override public void onTransitionReady(@NonNull IBinder transitionToken, TransitionInfo info, public void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); Loading @@ -131,22 +131,53 @@ public class Transitions extends ITransitionPlayer.Stub { + transitionToken); } mActiveTransitions.put(transitionToken, new ArrayList<>()); for (int i = 0; i < info.getChanges().size(); ++i) { final SurfaceControl leash = info.getChanges().get(i).getLeash(); boolean isOpening = isOpeningType(info.getType()); if (info.getRootLeash().isValid()) { t.show(info.getRootLeash()); } // changes should be ordered top-to-bottom in z for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); final SurfaceControl leash = change.getLeash(); final int mode = info.getChanges().get(i).getMode(); // Don't animate anything with an animating parent if (change.getParent() != null) { if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); if (isOpeningType(info.getType())) { } continue; } t.reparent(leash, info.getRootLeash()); t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, change.getEndAbsBounds().top - info.getRootOffset().y); // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); if (isOpening) { // put on top and fade in t.setLayer(leash, info.getChanges().size() - i); t.setAlpha(leash, 0.f); startExampleAnimation(transitionToken, leash, true /* show */); } else { // put on bottom and leave it visible without fade t.setLayer(leash, -i); t.setAlpha(leash, 1.f); } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) { if (!isOpeningType(info.getType())) { if (isOpening) { // put on bottom and leave visible without fade t.setLayer(leash, -i); } else { // put on top and fade out t.setLayer(leash, info.getChanges().size() - i); startExampleAnimation(transitionToken, leash, false /* show */); } } else { t.setLayer(leash, info.getChanges().size() - i); } } t.apply(); Loading
services/core/java/com/android/server/wm/Transition.java +146 −23 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import android.annotation.IntDef; import android.annotation.NonNull; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; Loading @@ -46,7 +47,6 @@ import com.android.internal.protolog.common.ProtoLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Set; /** * Represents a logical transition. Loading Loading @@ -90,6 +90,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private final TransitionController mController; private final BLASTSyncEngine mSyncEngine; /** * This is a leash to put animating surfaces into flatly without clipping/ordering issues. It * is a child of all the targets' shared ancestor. */ private SurfaceControl mRootLeash = null; /** * Contains change infos for both participants and all ancestors. We have to track ancestors * because they are all promotion candidates and thus we need their start-states Loading @@ -97,7 +103,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe */ final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>(); /** The collected participants in the transition. */ final ArraySet<WindowContainer> mParticipants = new ArraySet<>(); /** The final animation targets derived from participants after promotion. */ private ArraySet<WindowContainer> mTargets = null; private @TransitionState int mState = STATE_COLLECTING; private boolean mReadyCalled = false; Loading Loading @@ -190,6 +201,42 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (mState < STATE_PLAYING) { throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId); } final Point tmpPos = new Point(); // usually only size 1 final ArraySet<DisplayContent> displays = new ArraySet<>(); // Immediately apply all surface reparents, don't wait for pending/sync/etc. SurfaceControl.Transaction t = mController.mAtm.mWindowManager.mTransactionFactory.get(); for (int i = mTargets.size() - 1; i >= 0; --i) { final WindowContainer target = mTargets.valueAt(i); if (target.getParent() != null) { // Ensure surfaceControls are re-parented back into the hierarchy. t.reparent(target.getSurfaceControl(), target.getParent().getSurfaceControl()); target.getRelativePosition(tmpPos); t.setPosition(target.getSurfaceControl(), tmpPos.x, tmpPos.y); displays.add(target.getDisplayContent()); } } // Need to update layers on ALL displays (for now) since they were all paused while // the animation played. for (int i = displays.size() - 1; i >= 0; --i) { if (displays.valueAt(i) == null) continue; displays.valueAt(i).assignChildLayers(t); } // Also pro-actively hide going-invisible activity surfaces in same transaction to // prevent flickers due to reparenting and animation z-order mismatch. for (int i = mParticipants.size() - 1; i >= 0; --i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar == null || ar.mVisibleRequested || !ar.isVisible()) continue; t.hide(ar.getSurfaceControl()); } if (mRootLeash.isValid()) { t.remove(mRootLeash); } mRootLeash = null; t.apply(); t.close(); // Commit all going-invisible containers for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar == null || ar.mVisibleRequested) { Loading Loading @@ -234,7 +281,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mState = STATE_PLAYING; mController.moveToPlaying(this); final TransitionInfo info = calculateTransitionInfo(mType, mParticipants, mChanges); // Resolve the animating targets from the participants mTargets = calculateTargets(mParticipants, mChanges); final TransitionInfo info = calculateTransitionInfo(mType, mTargets, mChanges); mRootLeash = info.getRootLeash(); handleNonAppWindowsInTransition(displayId, mType, mFlags); Loading @@ -245,11 +296,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mController.getTransitionPlayer().onTransitionReady(this, info, transaction); } catch (RemoteException e) { // If there's an exception when trying to send the mergedTransaction to the // client, we should immediately apply it here so the transactions aren't lost. // client, we should finish and apply it here so the transactions aren't lost. transaction.apply(); finishTransition(); } } else { // No player registered, so just finish/apply immediately transaction.apply(); finishTransition(); } mSyncId = -1; } Loading Loading @@ -325,10 +379,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * * @return {@code true} if transition in target can be promoted to its parent. */ private static boolean canPromote( WindowContainer target, ArraySet<WindowContainer> topTargets) { private static boolean canPromote(WindowContainer target, ArraySet<WindowContainer> topTargets, ArrayMap<WindowContainer, ChangeInfo> changes) { final WindowContainer parent = target.getParent(); if (parent == null || !parent.canCreateRemoteAnimationTarget()) { final ChangeInfo parentChanges = parent != null ? changes.get(parent) : null; if (parent == null || !parent.canCreateRemoteAnimationTarget() || parentChanges == null || !parentChanges.hasChanged(parent)) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " SKIP: %s", parent == null ? "no parent" : ("parent can't be target " + parent)); return false; Loading Loading @@ -394,14 +450,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Go through each target until we find one that can be promoted. for (WindowContainer targ : topTargets) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " checking %s", targ); if (!canPromote(targ, topTargets)) { if (!canPromote(targ, topTargets, changes)) { continue; } final WindowContainer parent = targ.getParent(); // No obstructions found to promotion, so promote final WindowContainer parent = targ.getParent(); final ChangeInfo parentInfo = changes.get(parent); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " CAN PROMOTE: promoting to parent %s", parent); final ChangeInfo parentInfo = changes.get(parent); targets.add(parent); // Go through all children of newly-promoted container and remove them from the Loading Loading @@ -443,10 +499,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * animation targets to higher level in the window hierarchy if possible. */ @VisibleForTesting static TransitionInfo calculateTransitionInfo(int type, Set<WindowContainer> participants, @NonNull static ArraySet<WindowContainer> calculateTargets(ArraySet<WindowContainer> participants, ArrayMap<WindowContainer, ChangeInfo> changes) { final TransitionInfo out = new TransitionInfo(type); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start calculating TransitionInfo based on participants: %s", participants); Loading @@ -470,6 +525,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Search through ancestors to find the top-most participant (if one exists) WindowContainer topParent = null; tmpList.clear(); if (reportIfNotTop(wc)) { tmpList.add(wc); } for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) { if (participants.contains(p)) { topParent = p; Loading @@ -479,8 +537,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } if (topParent != null) { // There was an ancestor participant, so don't add wc to targets. However, continue // to add any always-report parents along the way. // There was an ancestor participant, so don't add wc to targets unless always- // report. Similarly, add any always-report parents along the way. for (int i = 0; i < tmpList.size(); ++i) { targets.add(tmpList.get(i)); final ChangeInfo info = changes.get(tmpList.get(i)); Loading Loading @@ -508,10 +566,70 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe while (tryPromote(topTargets, targets, changes)) { // Empty on purpose } return targets; } // Convert all the resolved ChangeInfos into a TransactionInfo object. for (int i = targets.size() - 1; i >= 0; --i) { final WindowContainer target = targets.valueAt(i); /** Add any of `members` within `root` to `out` in top-to-bottom z-order. */ private static void addMembersInOrder(WindowContainer root, ArraySet<WindowContainer> members, ArrayList<WindowContainer> out) { for (int i = root.getChildCount() - 1; i >= 0; --i) { final WindowContainer child = root.getChildAt(i); addMembersInOrder(child, members, out); if (members.contains(child)) { out.add(child); } } } /** * Construct a TransitionInfo object from a set of targets and changes. Also populates the * root surface. */ @VisibleForTesting @NonNull static TransitionInfo calculateTransitionInfo(int type, ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) { final TransitionInfo out = new TransitionInfo(type); if (targets.isEmpty()) { out.setRootLeash(new SurfaceControl(), 0, 0); return out; } // Find the top-most shared ancestor WindowContainer ancestor = targets.valueAt(0).getParent(); // Go up ancestor parent chain until all topTargets are descendants. ancestorLoop: while (ancestor != null) { for (int i = 1; i < targets.size(); ++i) { if (!targets.valueAt(i).isDescendantOf(ancestor)) { ancestor = ancestor.getParent(); continue ancestorLoop; } } break; } // Sort targets top-to-bottom in Z. ArrayList<WindowContainer> sortedTargets = new ArrayList<>(); addMembersInOrder(ancestor, targets, sortedTargets); // make leash based on highest (z-order) direct child of ancestor with a participant. WindowContainer leashReference = sortedTargets.get(0); while (leashReference.getParent() != ancestor) { leashReference = leashReference.getParent(); } final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName( "Transition Root: " + leashReference.getName()).build(); SurfaceControl.Transaction t = ancestor.mWmService.mTransactionFactory.get(); t.setLayer(rootLeash, leashReference.getLastLayer()); t.apply(); t.close(); 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) { final WindowContainer target = sortedTargets.get(i); final ChangeInfo info = changes.get(target); final TransitionInfo.Change change = new TransitionInfo.Change( target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken() Loading @@ -520,8 +638,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe change.setParent(info.mParent.mRemoteToken.toWindowContainerToken()); } change.setMode(info.getTransitMode(target)); change.setStartBounds(info.mAbsoluteBounds); change.setEndBounds(target.getBounds()); change.setStartAbsBounds(info.mAbsoluteBounds); change.setEndAbsBounds(target.getBounds()); change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left, target.getBounds().top - target.getParent().getBounds().top); out.addChange(change); } Loading @@ -543,23 +663,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // before change state boolean mVisible; int mWindowingMode; Rect mAbsoluteBounds; final Rect mAbsoluteBounds = new Rect(); ChangeInfo(@NonNull WindowContainer origState) { mVisible = origState.isVisibleRequested(); mWindowingMode = origState.getWindowingMode(); mAbsoluteBounds = origState.getBounds(); mAbsoluteBounds.set(origState.getBounds()); } @VisibleForTesting ChangeInfo(boolean visible, boolean existChange) { mVisible = visible; mAbsoluteBounds = new Rect(); mExistenceChanged = existChange; } boolean hasChanged(@NonNull WindowContainer newState) { return newState.isVisibleRequested() != mVisible // If it's invisible and hasn't changed visibility, always return false since even if // something changed, it wouldn't be a visible change. final boolean currVisible = newState.isVisibleRequested(); if (currVisible == mVisible && !mVisible) return false; return currVisible != mVisible // if mWindowingMode is 0, this container wasn't attached at collect time, so // assume no change in windowing-mode. || (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode) Loading
services/core/java/com/android/server/wm/TransitionController.java +9 −1 Original line number Diff line number Diff line Loading @@ -109,10 +109,18 @@ class TransitionController { return mCollectingTransition != null; } /** * @return {@code true} if transition is actively playing. This is not necessarily {@code true} * during collection. */ boolean isPlaying() { return !mPlayingTransitions.isEmpty(); } /** @return {@code true} if a transition is running */ boolean inTransition() { // TODO(shell-transitions): eventually properly support multiple return mCollectingTransition != null || !mPlayingTransitions.isEmpty(); return isCollecting() || isPlaying(); } /** @return {@code true} if wc is in a participant subtree */ Loading
services/core/java/com/android/server/wm/WindowContainer.java +7 −0 Original line number Diff line number Diff line Loading @@ -2089,6 +2089,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } void assignLayer(Transaction t, int layer) { // Don't assign layers while a transition animation is playing // TODO(b/173528115): establish robust best-practices around z-order fighting. if (mWmService.mAtmService.getTransitionController().isPlaying()) return; final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; if (mSurfaceControl != null && changed) { setLayer(t, layer); Loading @@ -2113,6 +2116,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSurfaceAnimator.setLayer(t, layer); } int getLastLayer() { return mLastLayer; } protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) { // Route through surface animator to accommodate that our surface control might be Loading