Merge "Initial implementation of merge-able transitions" into sc-dev
This commit is contained in:
@@ -40,7 +40,23 @@ oneway interface IRemoteTransition {
|
|||||||
/**
|
/**
|
||||||
* Starts a transition animation. Once complete, the implementation should call
|
* Starts a transition animation. Once complete, the implementation should call
|
||||||
* `finishCallback`.
|
* `finishCallback`.
|
||||||
|
*
|
||||||
|
* @param token An identifier for the transition that should be animated.
|
||||||
*/
|
*/
|
||||||
void startAnimation(in TransitionInfo info, in SurfaceControl.Transaction t,
|
void startAnimation(in IBinder token, in TransitionInfo info, in SurfaceControl.Transaction t,
|
||||||
|
in IRemoteTransitionFinishedCallback finishCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to merge a transition animation into the animation that is currently
|
||||||
|
* being played by this remote. If merge is not possible/supported, this should be a no-op.
|
||||||
|
* If it *is* merged, the implementation should call `finishCallback` immediately.
|
||||||
|
*
|
||||||
|
* @param transition An identifier for the transition that wants to be merged.
|
||||||
|
* @param mergeTarget The transition that is currently being animated by this remote.
|
||||||
|
* If it can be merged, call `finishCallback`; otherwise, do
|
||||||
|
* nothing.
|
||||||
|
*/
|
||||||
|
void mergeAnimation(in IBinder transition, in TransitionInfo info,
|
||||||
|
in SurfaceControl.Transaction t, in IBinder mergeTarget,
|
||||||
in IRemoteTransitionFinishedCallback finishCallback);
|
in IRemoteTransitionFinishedCallback finishCallback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,9 +47,12 @@ oneway interface ITransitionPlayer {
|
|||||||
* @param transitionToken An identifying token for the transition that is now ready to animate.
|
* @param transitionToken An identifying token for the transition that is now ready to animate.
|
||||||
* @param info A collection of all the changes encapsulated by this transition.
|
* @param info A collection of all the changes encapsulated by this transition.
|
||||||
* @param t A surface transaction containing the surface state prior to animating.
|
* @param t A surface transaction containing the surface state prior to animating.
|
||||||
|
* @param finishT A surface transaction that will reset parenting/layering and generally put
|
||||||
|
* surfaces into their final (post-transition) state. Apply this after playing
|
||||||
|
* the animation but before calling finish.
|
||||||
*/
|
*/
|
||||||
void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
|
void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
|
||||||
in SurfaceControl.Transaction t);
|
in SurfaceControl.Transaction t, in SurfaceControl.Transaction finishT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when something in WMCore requires a transition to play -- for example when an Activity
|
* Called when something in WMCore requires a transition to play -- for example when an Activity
|
||||||
|
|||||||
@@ -82,17 +82,38 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
|
|||||||
if (mRemote.asBinder() != null) {
|
if (mRemote.asBinder() != null) {
|
||||||
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
|
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
|
||||||
}
|
}
|
||||||
mRemote.startAnimation(info, t, cb);
|
mRemote.startAnimation(transition, info, t, cb);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
|
Log.e(Transitions.TAG, "Error running remote transition.", e);
|
||||||
if (mRemote.asBinder() != null) {
|
if (mRemote.asBinder() != null) {
|
||||||
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
|
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
|
||||||
}
|
}
|
||||||
Log.e(Transitions.TAG, "Error running remote transition.", e);
|
|
||||||
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
|
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||||
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
|
||||||
|
+ " transition %s for %s.", mRemote, transition);
|
||||||
|
|
||||||
|
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onTransitionFinished(WindowContainerTransaction wct) {
|
||||||
|
mMainExecutor.execute(
|
||||||
|
() -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
mRemote.mergeAnimation(transition, info, t, mergeTarget, cb);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(Transitions.TAG, "Error merging remote transition.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package com.android.wm.shell.transition;
|
package com.android.wm.shell.transition;
|
||||||
|
|
||||||
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -52,7 +50,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
private final ShellExecutor mMainExecutor;
|
private final ShellExecutor mMainExecutor;
|
||||||
|
|
||||||
/** Includes remotes explicitly requested by, eg, ActivityOptions */
|
/** Includes remotes explicitly requested by, eg, ActivityOptions */
|
||||||
private final ArrayMap<IBinder, IRemoteTransition> mPendingRemotes = new ArrayMap<>();
|
private final ArrayMap<IBinder, IRemoteTransition> mRequestedRemotes = new ArrayMap<>();
|
||||||
|
|
||||||
/** Ordered by specificity. Last filters will be checked first */
|
/** Ordered by specificity. Last filters will be checked first */
|
||||||
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
|
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
|
||||||
@@ -63,9 +61,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
@Override
|
@Override
|
||||||
@BinderThread
|
@BinderThread
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
mMainExecutor.execute(() -> {
|
mMainExecutor.execute(() -> mFilters.clear());
|
||||||
mFilters.clear();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,11 +92,16 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionMerged(@NonNull IBinder transition) {
|
||||||
|
mRequestedRemotes.remove(transition);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
@NonNull SurfaceControl.Transaction t,
|
@NonNull SurfaceControl.Transaction t,
|
||||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
IRemoteTransition pendingRemote = mPendingRemotes.remove(transition);
|
IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
|
||||||
if (pendingRemote == null) {
|
if (pendingRemote == null) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
|
||||||
+ "explicit remote, search filters for match for %s", transition, info);
|
+ "explicit remote, search filters for match for %s", transition, info);
|
||||||
@@ -110,6 +111,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
mFilters.get(i));
|
mFilters.get(i));
|
||||||
if (mFilters.get(i).first.matches(info)) {
|
if (mFilters.get(i).first.matches(info)) {
|
||||||
pendingRemote = mFilters.get(i).second;
|
pendingRemote = mFilters.get(i).second;
|
||||||
|
// Add to requested list so that it can be found for merge requests.
|
||||||
|
mRequestedRemotes.put(transition, pendingRemote);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,8 +125,10 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
final IRemoteTransition remote = pendingRemote;
|
final IRemoteTransition remote = pendingRemote;
|
||||||
final IBinder.DeathRecipient remoteDied = () -> {
|
final IBinder.DeathRecipient remoteDied = () -> {
|
||||||
Log.e(Transitions.TAG, "Remote transition died, finishing");
|
Log.e(Transitions.TAG, "Remote transition died, finishing");
|
||||||
mMainExecutor.execute(
|
mMainExecutor.execute(() -> {
|
||||||
() -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
|
mRequestedRemotes.remove(transition);
|
||||||
|
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
|
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
|
||||||
@Override
|
@Override
|
||||||
@@ -131,33 +136,66 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
if (remote.asBinder() != null) {
|
if (remote.asBinder() != null) {
|
||||||
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
|
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
|
||||||
}
|
}
|
||||||
mMainExecutor.execute(
|
mMainExecutor.execute(() -> {
|
||||||
() -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
|
mRequestedRemotes.remove(transition);
|
||||||
|
finishCallback.onTransitionFinished(wct, null /* wctCB */);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
if (remote.asBinder() != null) {
|
if (remote.asBinder() != null) {
|
||||||
remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
|
remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
|
||||||
}
|
}
|
||||||
remote.startAnimation(info, t, cb);
|
remote.startAnimation(transition, info, t, cb);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
|
Log.e(Transitions.TAG, "Error running remote transition.", e);
|
||||||
if (remote.asBinder() != null) {
|
if (remote.asBinder() != null) {
|
||||||
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
|
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
|
||||||
}
|
}
|
||||||
Log.e(Transitions.TAG, "Error running remote transition.", e);
|
mRequestedRemotes.remove(transition);
|
||||||
mMainExecutor.execute(
|
mMainExecutor.execute(
|
||||||
() -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
|
() -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||||
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget);
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s",
|
||||||
|
transition, remote);
|
||||||
|
if (remote == null) return;
|
||||||
|
|
||||||
|
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onTransitionFinished(WindowContainerTransaction wct) {
|
||||||
|
mMainExecutor.execute(() -> {
|
||||||
|
if (!mRequestedRemotes.containsKey(mergeTarget)) {
|
||||||
|
Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
|
||||||
|
+ "transition it was supposed to merge into). This usually means "
|
||||||
|
+ "that the mergeTarget's RemoteTransition impl erroneously "
|
||||||
|
+ "accepted/ran the merge request after finishing the mergeTarget");
|
||||||
|
}
|
||||||
|
finishCallback.onTransitionFinished(wct, null /* wctCB */);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
remote.mergeAnimation(transition, info, t, mergeTarget, cb);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||||
@Nullable TransitionRequestInfo request) {
|
@Nullable TransitionRequestInfo request) {
|
||||||
IRemoteTransition remote = request.getRemoteTransition();
|
IRemoteTransition remote = request.getRemoteTransition();
|
||||||
if (remote == null) return null;
|
if (remote == null) return null;
|
||||||
mPendingRemotes.put(transition, remote);
|
mRequestedRemotes.put(transition, remote);
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
|
||||||
+ " for %s: %s", transition, remote);
|
+ " for %s: %s", transition, remote);
|
||||||
return new WindowContainerTransaction();
|
return new WindowContainerTransaction();
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import android.os.IBinder;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -91,11 +90,16 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
private float mTransitionAnimationScaleSetting = 1.0f;
|
private float mTransitionAnimationScaleSetting = 1.0f;
|
||||||
|
|
||||||
private static final class ActiveTransition {
|
private static final class ActiveTransition {
|
||||||
TransitionHandler mFirstHandler = null;
|
IBinder mToken = null;
|
||||||
|
TransitionHandler mHandler = null;
|
||||||
|
boolean mMerged = false;
|
||||||
|
TransitionInfo mInfo = null;
|
||||||
|
SurfaceControl.Transaction mStartT = null;
|
||||||
|
SurfaceControl.Transaction mFinishT = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Keeps track of currently tracked transitions and all the animations associated with each */
|
/** Keeps track of currently playing transitions in the order of receipt. */
|
||||||
private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
|
private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
|
||||||
|
|
||||||
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
|
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
|
||||||
@NonNull Context context, @NonNull ShellExecutor mainExecutor,
|
@NonNull Context context, @NonNull ShellExecutor mainExecutor,
|
||||||
@@ -226,7 +230,7 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
* type, their transit mode, and their destination z-order.
|
* type, their transit mode, and their destination z-order.
|
||||||
*/
|
*/
|
||||||
private static void setupStartState(@NonNull TransitionInfo info,
|
private static void setupStartState(@NonNull TransitionInfo info,
|
||||||
@NonNull SurfaceControl.Transaction t) {
|
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
|
||||||
boolean isOpening = isOpeningType(info.getType());
|
boolean isOpening = isOpeningType(info.getType());
|
||||||
if (info.getRootLeash().isValid()) {
|
if (info.getRootLeash().isValid()) {
|
||||||
t.show(info.getRootLeash());
|
t.show(info.getRootLeash());
|
||||||
@@ -270,6 +274,8 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
t.setAlpha(leash, 1.f);
|
t.setAlpha(leash, 1.f);
|
||||||
} else {
|
} else {
|
||||||
t.setAlpha(leash, 0.f);
|
t.setAlpha(leash, 0.f);
|
||||||
|
// fix alpha in finish transaction in case the animator itself no-ops.
|
||||||
|
finishT.setAlpha(leash, 1.f);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// put on bottom and leave it visible
|
// put on bottom and leave it visible
|
||||||
@@ -290,16 +296,24 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int findActiveTransition(IBinder token) {
|
||||||
|
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
|
||||||
|
if (mActiveTransitions.get(i).mToken == token) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
|
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
|
||||||
@NonNull SurfaceControl.Transaction t) {
|
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
|
||||||
transitionToken, info);
|
transitionToken, info);
|
||||||
final ActiveTransition active = mActiveTransitions.get(transitionToken);
|
final int activeIdx = findActiveTransition(transitionToken);
|
||||||
if (active == null) {
|
if (activeIdx < 0) {
|
||||||
throw new IllegalStateException("Got transitionReady for non-active transition "
|
throw new IllegalStateException("Got transitionReady for non-active transition "
|
||||||
+ transitionToken + ". expecting one of "
|
+ transitionToken + ". expecting one of "
|
||||||
+ Arrays.toString(mActiveTransitions.keySet().toArray()));
|
+ Arrays.toString(mActiveTransitions.stream().map(
|
||||||
|
activeTransition -> activeTransition.mToken).toArray()));
|
||||||
}
|
}
|
||||||
if (!info.getRootLeash().isValid()) {
|
if (!info.getRootLeash().isValid()) {
|
||||||
// Invalid root-leash implies that the transition is empty/no-op, so just do
|
// Invalid root-leash implies that the transition is empty/no-op, so just do
|
||||||
@@ -307,30 +321,62 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
|
||||||
transitionToken, info);
|
transitionToken, info);
|
||||||
t.apply();
|
t.apply();
|
||||||
onFinish(transitionToken, null /* wct */, null /* wctCB */);
|
onAbort(transitionToken);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setupStartState(info, t);
|
final ActiveTransition active = mActiveTransitions.get(activeIdx);
|
||||||
|
active.mInfo = info;
|
||||||
|
active.mStartT = t;
|
||||||
|
active.mFinishT = finishT;
|
||||||
|
if (activeIdx > 0) {
|
||||||
|
// This is now playing at the same time as an existing animation, so try merging it.
|
||||||
|
attemptMergeTransition(mActiveTransitions.get(0), active);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The normal case, just play it.
|
||||||
|
playTransition(active);
|
||||||
|
}
|
||||||
|
|
||||||
final TransitionFinishCallback finishCb = (wct, cb) -> onFinish(transitionToken, wct, cb);
|
/**
|
||||||
// If a handler chose to uniquely run this animation, try delegating to it.
|
* Attempt to merge by delegating the transition start to the handler of the currently
|
||||||
if (active.mFirstHandler != null) {
|
* playing transition.
|
||||||
|
*/
|
||||||
|
void attemptMergeTransition(@NonNull ActiveTransition playing,
|
||||||
|
@NonNull ActiveTransition merging) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
|
||||||
|
+ " another transition %s is still animating. Notify the animating transition"
|
||||||
|
+ " in case they can be merged", merging.mToken, playing.mToken);
|
||||||
|
playing.mHandler.mergeAnimation(merging.mToken, merging.mInfo, merging.mStartT,
|
||||||
|
playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
|
||||||
|
return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
|
||||||
|
(wct, cb) -> onFinish(active.mToken, wct, cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
void playTransition(@NonNull ActiveTransition active) {
|
||||||
|
setupStartState(active.mInfo, active.mStartT, active.mFinishT);
|
||||||
|
|
||||||
|
// If a handler already chose to run this animation, try delegating to it first.
|
||||||
|
if (active.mHandler != null) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
|
||||||
active.mFirstHandler);
|
active.mHandler);
|
||||||
if (active.mFirstHandler.startAnimation(transitionToken, info, t, finishCb)) {
|
if (startAnimation(active, active.mHandler)) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise give every other handler a chance (in order)
|
// Otherwise give every other handler a chance (in order)
|
||||||
for (int i = mHandlers.size() - 1; i >= 0; --i) {
|
for (int i = mHandlers.size() - 1; i >= 0; --i) {
|
||||||
if (mHandlers.get(i) == active.mFirstHandler) continue;
|
if (mHandlers.get(i) == active.mHandler) continue;
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
|
||||||
mHandlers.get(i));
|
mHandlers.get(i));
|
||||||
if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishCb)) {
|
if (startAnimation(active, mHandlers.get(i))) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
|
||||||
mHandlers.get(i));
|
mHandlers.get(i));
|
||||||
|
active.mHandler = mHandlers.get(i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,23 +384,107 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
"This shouldn't happen, maybe the default handler is broken.");
|
"This shouldn't happen, maybe the default handler is broken.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFinish(IBinder transition, @Nullable WindowContainerTransaction wct,
|
/** Special version of finish just for dealing with no-op/invalid transitions. */
|
||||||
|
private void onAbort(IBinder transition) {
|
||||||
|
final int activeIdx = findActiveTransition(transition);
|
||||||
|
if (activeIdx < 0) return;
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||||
|
"Transition animation aborted due to no-op, notifying core %s", transition);
|
||||||
|
mActiveTransitions.remove(activeIdx);
|
||||||
|
mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFinish(IBinder transition,
|
||||||
|
@Nullable WindowContainerTransaction wct,
|
||||||
@Nullable WindowContainerTransactionCallback wctCB) {
|
@Nullable WindowContainerTransactionCallback wctCB) {
|
||||||
if (!mActiveTransitions.containsKey(transition)) {
|
int activeIdx = findActiveTransition(transition);
|
||||||
Log.e(TAG, "Trying to finish a non-running transition. Maybe remote crashed?");
|
if (activeIdx < 0) {
|
||||||
|
Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
|
||||||
|
+ " a handler didn't properly deal with a merge.", new RuntimeException());
|
||||||
|
return;
|
||||||
|
} else if (activeIdx > 0) {
|
||||||
|
// This transition was merged.
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
|
||||||
|
transition);
|
||||||
|
final ActiveTransition active = mActiveTransitions.get(activeIdx);
|
||||||
|
active.mMerged = true;
|
||||||
|
if (active.mHandler != null) {
|
||||||
|
active.mHandler.onTransitionMerged(active.mToken);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||||
"Transition animations finished, notifying core %s", transition);
|
"Transition animation finished, notifying core %s", transition);
|
||||||
mActiveTransitions.remove(transition);
|
// Merge all relevant transactions together
|
||||||
|
SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
|
||||||
|
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
|
||||||
|
final ActiveTransition toMerge = mActiveTransitions.get(iA);
|
||||||
|
if (!toMerge.mMerged) break;
|
||||||
|
// Include start. It will be a no-op if it was already applied. Otherwise, we need it
|
||||||
|
// to maintain consistent state.
|
||||||
|
fullFinish.merge(mActiveTransitions.get(iA).mStartT);
|
||||||
|
fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
|
||||||
|
}
|
||||||
|
fullFinish.apply();
|
||||||
|
// Now perform all the finishes.
|
||||||
|
mActiveTransitions.remove(activeIdx);
|
||||||
mOrganizer.finishTransition(transition, wct, wctCB);
|
mOrganizer.finishTransition(transition, wct, wctCB);
|
||||||
|
while (activeIdx < mActiveTransitions.size()) {
|
||||||
|
if (!mActiveTransitions.get(activeIdx).mMerged) break;
|
||||||
|
ActiveTransition merged = mActiveTransitions.remove(activeIdx);
|
||||||
|
mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
|
||||||
|
}
|
||||||
|
if (mActiveTransitions.size() <= activeIdx) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
|
||||||
|
+ "finished");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Start animating the next active transition
|
||||||
|
final ActiveTransition next = mActiveTransitions.get(activeIdx);
|
||||||
|
if (next.mInfo == null) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transition after one"
|
||||||
|
+ " finished, but it isn't ready yet.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transitions after one"
|
||||||
|
+ " finished, so start the next one.");
|
||||||
|
playTransition(next);
|
||||||
|
// Now try to merge the rest of the transitions (re-acquire activeIdx since next may have
|
||||||
|
// finished immediately)
|
||||||
|
activeIdx = findActiveTransition(next.mToken);
|
||||||
|
if (activeIdx < 0) {
|
||||||
|
// This means 'next' finished immediately and thus re-entered this function. Since
|
||||||
|
// that is the case, just return here since all relevant logic has already run in the
|
||||||
|
// re-entered call.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This logic is also convoluted because 'next' may finish immediately in response to any of
|
||||||
|
// the merge requests (eg. if it decided to "cancel" itself).
|
||||||
|
int mergeIdx = activeIdx + 1;
|
||||||
|
while (mergeIdx < mActiveTransitions.size()) {
|
||||||
|
ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
|
||||||
|
if (mergeCandidate.mMerged) {
|
||||||
|
throw new IllegalStateException("Can't merge a transition after not-merging"
|
||||||
|
+ " a preceding one.");
|
||||||
|
}
|
||||||
|
attemptMergeTransition(next, mergeCandidate);
|
||||||
|
mergeIdx = findActiveTransition(mergeCandidate.mToken);
|
||||||
|
if (mergeIdx < 0) {
|
||||||
|
// This means 'next' finished immediately and thus re-entered this function. Since
|
||||||
|
// that is the case, just return here since all relevant logic has already run in
|
||||||
|
// the re-entered call.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
++mergeIdx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestStartTransition(@NonNull IBinder transitionToken,
|
void requestStartTransition(@NonNull IBinder transitionToken,
|
||||||
@Nullable TransitionRequestInfo request) {
|
@Nullable TransitionRequestInfo request) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
|
||||||
transitionToken, request);
|
transitionToken, request);
|
||||||
if (mActiveTransitions.containsKey(transitionToken)) {
|
if (findActiveTransition(transitionToken) >= 0) {
|
||||||
throw new RuntimeException("Transition already started " + transitionToken);
|
throw new RuntimeException("Transition already started " + transitionToken);
|
||||||
}
|
}
|
||||||
final ActiveTransition active = new ActiveTransition();
|
final ActiveTransition active = new ActiveTransition();
|
||||||
@@ -362,23 +492,23 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
for (int i = mHandlers.size() - 1; i >= 0; --i) {
|
for (int i = mHandlers.size() - 1; i >= 0; --i) {
|
||||||
wct = mHandlers.get(i).handleRequest(transitionToken, request);
|
wct = mHandlers.get(i).handleRequest(transitionToken, request);
|
||||||
if (wct != null) {
|
if (wct != null) {
|
||||||
active.mFirstHandler = mHandlers.get(i);
|
active.mHandler = mHandlers.get(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IBinder transition = mOrganizer.startTransition(
|
active.mToken = mOrganizer.startTransition(
|
||||||
request.getType(), transitionToken, wct);
|
request.getType(), transitionToken, wct);
|
||||||
mActiveTransitions.put(transition, active);
|
mActiveTransitions.add(active);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start a new transition directly. */
|
/** Start a new transition directly. */
|
||||||
public IBinder startTransition(@WindowManager.TransitionType int type,
|
public IBinder startTransition(@WindowManager.TransitionType int type,
|
||||||
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
|
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
|
||||||
final ActiveTransition active = new ActiveTransition();
|
final ActiveTransition active = new ActiveTransition();
|
||||||
active.mFirstHandler = handler;
|
active.mHandler = handler;
|
||||||
IBinder transition = mOrganizer.startTransition(type, null /* token */, wct);
|
active.mToken = mOrganizer.startTransition(type, null /* token */, wct);
|
||||||
mActiveTransitions.put(transition, active);
|
mActiveTransitions.add(active);
|
||||||
return transition;
|
return active.mToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -414,6 +544,32 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
@NonNull SurfaceControl.Transaction t,
|
@NonNull SurfaceControl.Transaction t,
|
||||||
@NonNull TransitionFinishCallback finishCallback);
|
@NonNull TransitionFinishCallback finishCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to merge a different transition's animation into an animation that this handler
|
||||||
|
* is currently playing. If a merge is not possible/supported, this should be a no-op.
|
||||||
|
*
|
||||||
|
* This gets called if another transition becomes ready while this handler is still playing
|
||||||
|
* an animation. This is called regardless of whether this handler claims to support that
|
||||||
|
* particular transition or not.
|
||||||
|
*
|
||||||
|
* When this happens, there are 2 options:
|
||||||
|
* 1. Do nothing. This effectively rejects the merge request. This is the "safest" option.
|
||||||
|
* 2. Merge the incoming transition into this one. The implementation is up to this
|
||||||
|
* handler. To indicate that this handler has "consumed" the merge transition, it
|
||||||
|
* must call the finishCallback immediately, or at-least before the original
|
||||||
|
* transition's finishCallback is called.
|
||||||
|
*
|
||||||
|
* @param transition This is the transition that wants to be merged.
|
||||||
|
* @param info Information about what is changing in the transition.
|
||||||
|
* @param t Contains surface changes that resulted from the transition.
|
||||||
|
* @param mergeTarget This is the transition that we are attempting to merge with (ie. the
|
||||||
|
* one this handler is currently already animating).
|
||||||
|
* @param finishCallback Call this if merged. This MUST be called on main thread.
|
||||||
|
*/
|
||||||
|
default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||||
|
@NonNull TransitionFinishCallback finishCallback) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Potentially handles a startTransition request.
|
* Potentially handles a startTransition request.
|
||||||
*
|
*
|
||||||
@@ -426,6 +582,12 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||||
@NonNull TransitionRequestInfo request);
|
@NonNull TransitionRequestInfo request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a transition which was already "claimed" by this handler has been merged
|
||||||
|
* into another animation. Gives this handler a chance to clean-up any expectations.
|
||||||
|
*/
|
||||||
|
default void onTransitionMerged(@NonNull IBinder transition) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets transition animation scale settings value to handler.
|
* Sets transition animation scale settings value to handler.
|
||||||
*
|
*
|
||||||
@@ -438,10 +600,10 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
|
private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
|
||||||
@Override
|
@Override
|
||||||
public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
|
public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
|
||||||
SurfaceControl.Transaction transaction) throws RemoteException {
|
SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
|
||||||
mMainExecutor.execute(() -> {
|
throws RemoteException {
|
||||||
Transitions.this.onTransitionReady(iBinder, transitionInfo, transaction);
|
mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
|
||||||
});
|
iBinder, transitionInfo, t, finishT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -332,11 +332,18 @@ public class SplitTransitionTests extends ShellTestCase {
|
|||||||
final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
|
final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
public void startAnimation(IBinder transition, TransitionInfo info,
|
||||||
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
|
||||||
|
throws RemoteException {
|
||||||
mCalled = true;
|
mCalled = true;
|
||||||
finishCallback.onTransitionFinished(mRemoteFinishWCT);
|
finishCallback.onTransitionFinished(mRemoteFinishWCT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
|
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ public class ShellTransitionTests {
|
|||||||
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
|
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
|
||||||
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
|
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
|
||||||
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
||||||
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
|
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
assertEquals(1, mDefaultHandler.activeCount());
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
mDefaultHandler.finishAll();
|
mDefaultHandler.finishAll();
|
||||||
mMainExecutor.flushAll();
|
mMainExecutor.flushAll();
|
||||||
@@ -155,7 +156,8 @@ public class ShellTransitionTests {
|
|||||||
transitions.requestStartTransition(transitToken,
|
transitions.requestStartTransition(transitToken,
|
||||||
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
|
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
|
||||||
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull());
|
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull());
|
||||||
transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class));
|
transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
assertEquals(1, mDefaultHandler.activeCount());
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
assertEquals(0, testHandler.activeCount());
|
assertEquals(0, testHandler.activeCount());
|
||||||
mDefaultHandler.finishAll();
|
mDefaultHandler.finishAll();
|
||||||
@@ -168,7 +170,8 @@ public class ShellTransitionTests {
|
|||||||
new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */));
|
new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */));
|
||||||
verify(mOrganizer, times(1)).startTransition(
|
verify(mOrganizer, times(1)).startTransition(
|
||||||
eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT));
|
eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT));
|
||||||
transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class));
|
transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
assertEquals(1, mDefaultHandler.activeCount());
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
assertEquals(0, testHandler.activeCount());
|
assertEquals(0, testHandler.activeCount());
|
||||||
mDefaultHandler.finishAll();
|
mDefaultHandler.finishAll();
|
||||||
@@ -185,7 +188,8 @@ public class ShellTransitionTests {
|
|||||||
eq(TRANSIT_CHANGE), eq(transitToken), eq(handlerWCT));
|
eq(TRANSIT_CHANGE), eq(transitToken), eq(handlerWCT));
|
||||||
TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||||
.addChange(TRANSIT_CHANGE).build();
|
.addChange(TRANSIT_CHANGE).build();
|
||||||
transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class));
|
transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
assertEquals(0, mDefaultHandler.activeCount());
|
assertEquals(0, mDefaultHandler.activeCount());
|
||||||
assertEquals(1, testHandler.activeCount());
|
assertEquals(1, testHandler.activeCount());
|
||||||
assertEquals(0, topHandler.activeCount());
|
assertEquals(0, topHandler.activeCount());
|
||||||
@@ -203,11 +207,18 @@ public class ShellTransitionTests {
|
|||||||
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
|
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
|
||||||
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
|
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
public void startAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t,
|
||||||
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
||||||
remoteCalled[0] = true;
|
remoteCalled[0] = true;
|
||||||
finishCallback.onTransitionFinished(remoteFinishWCT);
|
finishCallback.onTransitionFinished(remoteFinishWCT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
|
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
IBinder transitToken = new Binder();
|
IBinder transitToken = new Binder();
|
||||||
transitions.requestStartTransition(transitToken,
|
transitions.requestStartTransition(transitToken,
|
||||||
@@ -215,7 +226,8 @@ public class ShellTransitionTests {
|
|||||||
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
|
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
|
||||||
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
|
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
|
||||||
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
||||||
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
|
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
assertEquals(0, mDefaultHandler.activeCount());
|
assertEquals(0, mDefaultHandler.activeCount());
|
||||||
assertTrue(remoteCalled[0]);
|
assertTrue(remoteCalled[0]);
|
||||||
mDefaultHandler.finishAll();
|
mDefaultHandler.finishAll();
|
||||||
@@ -269,11 +281,18 @@ public class ShellTransitionTests {
|
|||||||
final boolean[] remoteCalled = new boolean[]{false};
|
final boolean[] remoteCalled = new boolean[]{false};
|
||||||
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
|
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
public void startAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t,
|
||||||
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
||||||
remoteCalled[0] = true;
|
remoteCalled[0] = true;
|
||||||
finishCallback.onTransitionFinished(null /* wct */);
|
finishCallback.onTransitionFinished(null /* wct */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
|
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TransitionFilter filter = new TransitionFilter();
|
TransitionFilter filter = new TransitionFilter();
|
||||||
@@ -290,7 +309,8 @@ public class ShellTransitionTests {
|
|||||||
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
|
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
|
||||||
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
|
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
|
||||||
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
||||||
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
|
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
assertEquals(0, mDefaultHandler.activeCount());
|
assertEquals(0, mDefaultHandler.activeCount());
|
||||||
assertTrue(remoteCalled[0]);
|
assertTrue(remoteCalled[0]);
|
||||||
mDefaultHandler.finishAll();
|
mDefaultHandler.finishAll();
|
||||||
@@ -308,11 +328,18 @@ public class ShellTransitionTests {
|
|||||||
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
|
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
|
||||||
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
|
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
public void startAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t,
|
||||||
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
||||||
remoteCalled[0] = true;
|
remoteCalled[0] = true;
|
||||||
finishCallback.onTransitionFinished(remoteFinishWCT);
|
finishCallback.onTransitionFinished(remoteFinishWCT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
|
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
|
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
|
||||||
@@ -336,6 +363,86 @@ public class ShellTransitionTests {
|
|||||||
mock(SurfaceControl.Transaction.class), testFinish));
|
mock(SurfaceControl.Transaction.class), testFinish));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransitionQueueing() {
|
||||||
|
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
||||||
|
mMainExecutor, mAnimExecutor);
|
||||||
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
|
IBinder transitToken1 = new Binder();
|
||||||
|
transitions.requestStartTransition(transitToken1,
|
||||||
|
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
|
||||||
|
TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
|
||||||
|
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
||||||
|
transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
|
|
||||||
|
IBinder transitToken2 = new Binder();
|
||||||
|
transitions.requestStartTransition(transitToken2,
|
||||||
|
new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
|
||||||
|
TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
|
||||||
|
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
||||||
|
transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
|
// default handler doesn't merge by default, so it shouldn't increment active count.
|
||||||
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
|
assertEquals(0, mDefaultHandler.mergeCount());
|
||||||
|
verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any());
|
||||||
|
verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
|
||||||
|
|
||||||
|
mDefaultHandler.finishAll();
|
||||||
|
mMainExecutor.flushAll();
|
||||||
|
// first transition finished
|
||||||
|
verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any());
|
||||||
|
verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
|
||||||
|
// But now the "queued" transition is running
|
||||||
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
|
|
||||||
|
mDefaultHandler.finishAll();
|
||||||
|
mMainExecutor.flushAll();
|
||||||
|
verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransitionMerging() {
|
||||||
|
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
||||||
|
mMainExecutor, mAnimExecutor);
|
||||||
|
mDefaultHandler.setSimulateMerge(true);
|
||||||
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
|
IBinder transitToken1 = new Binder();
|
||||||
|
transitions.requestStartTransition(transitToken1,
|
||||||
|
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
|
||||||
|
TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
|
||||||
|
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
||||||
|
transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
|
|
||||||
|
IBinder transitToken2 = new Binder();
|
||||||
|
transitions.requestStartTransition(transitToken2,
|
||||||
|
new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
|
||||||
|
TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
|
||||||
|
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
|
||||||
|
transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class),
|
||||||
|
mock(SurfaceControl.Transaction.class));
|
||||||
|
// it should still only have 1 active, but then show 1 merged
|
||||||
|
assertEquals(1, mDefaultHandler.activeCount());
|
||||||
|
assertEquals(1, mDefaultHandler.mergeCount());
|
||||||
|
verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any());
|
||||||
|
// We don't tell organizer it is finished yet (since we still want to maintain ordering)
|
||||||
|
verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
|
||||||
|
|
||||||
|
mDefaultHandler.finishAll();
|
||||||
|
mMainExecutor.flushAll();
|
||||||
|
// transition + merged all finished.
|
||||||
|
verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any());
|
||||||
|
verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any());
|
||||||
|
// Make sure nothing was queued
|
||||||
|
assertEquals(0, mDefaultHandler.activeCount());
|
||||||
|
}
|
||||||
|
|
||||||
class TransitionInfoBuilder {
|
class TransitionInfoBuilder {
|
||||||
final TransitionInfo mInfo;
|
final TransitionInfo mInfo;
|
||||||
|
|
||||||
@@ -364,7 +471,9 @@ public class ShellTransitionTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TestTransitionHandler implements Transitions.TransitionHandler {
|
class TestTransitionHandler implements Transitions.TransitionHandler {
|
||||||
final ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
|
ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
|
||||||
|
final ArrayList<IBinder> mMerged = new ArrayList<>();
|
||||||
|
boolean mSimulateMerge = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
@@ -374,6 +483,15 @@ public class ShellTransitionTests {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||||
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
if (!mSimulateMerge) return;
|
||||||
|
mMerged.add(transition);
|
||||||
|
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||||
@@ -381,16 +499,25 @@ public class ShellTransitionTests {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSimulateMerge(boolean sim) {
|
||||||
|
mSimulateMerge = sim;
|
||||||
|
}
|
||||||
|
|
||||||
void finishAll() {
|
void finishAll() {
|
||||||
for (int i = mFinishes.size() - 1; i >= 0; --i) {
|
final ArrayList<Transitions.TransitionFinishCallback> finishes = mFinishes;
|
||||||
mFinishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
|
mFinishes = new ArrayList<>();
|
||||||
|
for (int i = finishes.size() - 1; i >= 0; --i) {
|
||||||
|
finishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
|
||||||
}
|
}
|
||||||
mFinishes.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int activeCount() {
|
int activeCount() {
|
||||||
return mFinishes.size();
|
return mFinishes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mergeCount() {
|
||||||
|
return mMerged.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SurfaceControl createMockSurface(boolean valid) {
|
private static SurfaceControl createMockSurface(boolean valid) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
|||||||
import static android.view.WindowManager.TransitionOldType;
|
import static android.view.WindowManager.TransitionOldType;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
||||||
|
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.IRemoteAnimationFinishedCallback;
|
import android.view.IRemoteAnimationFinishedCallback;
|
||||||
@@ -153,7 +154,8 @@ public class RemoteAnimationAdapterCompat {
|
|||||||
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
|
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
|
||||||
return new IRemoteTransition.Stub() {
|
return new IRemoteTransition.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
public void startAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t,
|
||||||
IRemoteTransitionFinishedCallback finishCallback) {
|
IRemoteTransitionFinishedCallback finishCallback) {
|
||||||
final RemoteAnimationTargetCompat[] appsCompat =
|
final RemoteAnimationTargetCompat[] appsCompat =
|
||||||
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
|
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
|
||||||
@@ -256,6 +258,14 @@ public class RemoteAnimationAdapterCompat {
|
|||||||
appsCompat, wallpapersCompat, nonAppsCompat,
|
appsCompat, wallpapersCompat, nonAppsCompat,
|
||||||
animationFinishedCallback);
|
animationFinishedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(IBinder token, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
|
IRemoteTransitionFinishedCallback finishCallback) {
|
||||||
|
// TODO: hook up merge to recents onTaskAppeared if applicable. Until then, ignore
|
||||||
|
// any incoming merges.
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -65,7 +66,8 @@ public class RemoteTransitionCompat implements Parcelable {
|
|||||||
@NonNull Executor executor) {
|
@NonNull Executor executor) {
|
||||||
mTransition = new IRemoteTransition.Stub() {
|
mTransition = new IRemoteTransition.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
public void startAnimation(IBinder transition, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t,
|
||||||
IRemoteTransitionFinishedCallback finishedCallback) {
|
IRemoteTransitionFinishedCallback finishedCallback) {
|
||||||
final Runnable finishAdapter = () -> {
|
final Runnable finishAdapter = () -> {
|
||||||
try {
|
try {
|
||||||
@@ -74,7 +76,22 @@ public class RemoteTransitionCompat implements Parcelable {
|
|||||||
Log.e(TAG, "Failed to call transition finished callback", e);
|
Log.e(TAG, "Failed to call transition finished callback", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
executor.execute(() -> runner.startAnimation(info, t, finishAdapter));
|
executor.execute(() -> runner.startAnimation(transition, info, t, finishAdapter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
|
IRemoteTransitionFinishedCallback finishedCallback) {
|
||||||
|
final Runnable finishAdapter = () -> {
|
||||||
|
try {
|
||||||
|
finishedCallback.onTransitionFinished(null /* wct */);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Failed to call transition finished callback", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
executor.execute(() -> runner.mergeAnimation(transition, info, t, mergeTarget,
|
||||||
|
finishAdapter));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -84,7 +101,8 @@ public class RemoteTransitionCompat implements Parcelable {
|
|||||||
RecentsAnimationControllerCompat controller) {
|
RecentsAnimationControllerCompat controller) {
|
||||||
mTransition = new IRemoteTransition.Stub() {
|
mTransition = new IRemoteTransition.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
public void startAnimation(IBinder transition, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t,
|
||||||
IRemoteTransitionFinishedCallback finishedCallback) {
|
IRemoteTransitionFinishedCallback finishedCallback) {
|
||||||
final RemoteAnimationTargetCompat[] apps =
|
final RemoteAnimationTargetCompat[] apps =
|
||||||
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
|
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
|
||||||
@@ -113,6 +131,13 @@ public class RemoteTransitionCompat implements Parcelable {
|
|||||||
recents.onAnimationStart(wrapControl, apps, wallpapers, new Rect(0, 0, 0, 0),
|
recents.onAnimationStart(wrapControl, apps, wallpapers, new Rect(0, 0, 0, 0),
|
||||||
new Rect());
|
new Rect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
|
IRemoteTransitionFinishedCallback finishedCallback) {
|
||||||
|
// TODO: hook up merge to onTaskAppeared. Until then, just ignore incoming merges.
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.shared.system;
|
package com.android.systemui.shared.system;
|
||||||
|
|
||||||
|
import android.os.IBinder;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.window.TransitionInfo;
|
import android.window.TransitionInfo;
|
||||||
|
|
||||||
@@ -25,6 +26,17 @@ public interface RemoteTransitionRunner {
|
|||||||
* Starts a transition animation. Once complete, the implementation should call
|
* Starts a transition animation. Once complete, the implementation should call
|
||||||
* `finishCallback`.
|
* `finishCallback`.
|
||||||
*/
|
*/
|
||||||
void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
|
void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
|
||||||
Runnable finishCallback);
|
Runnable finishCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to merge a transition into the currently-running animation. If merge is not
|
||||||
|
* possible/supported, this should do nothing. Otherwise, the implementation should call
|
||||||
|
* `finishCallback` immediately to indicate that it merged the transition.
|
||||||
|
*
|
||||||
|
* @param transition The transition that wants to be merged into the running animation.
|
||||||
|
* @param mergeTarget The transition to merge into (that this runner is currently animating).
|
||||||
|
*/
|
||||||
|
default void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||||
|
SurfaceControl.Transaction t, IBinder mergeTarget, Runnable finishCallback) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,11 +106,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
private final BLASTSyncEngine mSyncEngine;
|
private final BLASTSyncEngine mSyncEngine;
|
||||||
private IRemoteTransition mRemoteTransition = null;
|
private IRemoteTransition mRemoteTransition = null;
|
||||||
|
|
||||||
/**
|
/** Only use for clean-up after binder death! */
|
||||||
* This is a leash to put animating surfaces into flatly without clipping/ordering issues. It
|
private SurfaceControl.Transaction mStartTransaction = null;
|
||||||
* is a child of all the targets' shared ancestor.
|
private SurfaceControl.Transaction mFinishTransaction = null;
|
||||||
*/
|
|
||||||
private SurfaceControl mRootLeash = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains change infos for both participants and all ancestors. We have to track ancestors
|
* Contains change infos for both participants and all ancestors. We have to track ancestors
|
||||||
@@ -229,16 +227,16 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
setReady(true);
|
setReady(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The transition has finished animating and is ready to finalize WM state */
|
/**
|
||||||
void finishTransition() {
|
* Build a transaction that "resets" all the re-parenting and layer changes. This is
|
||||||
if (mState < STATE_PLAYING) {
|
* intended to be applied at the end of the transition but before the finish callback. This
|
||||||
throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
|
* 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) {
|
||||||
final Point tmpPos = new Point();
|
final Point tmpPos = new Point();
|
||||||
// usually only size 1
|
// usually only size 1
|
||||||
final ArraySet<DisplayContent> displays = new ArraySet<>();
|
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) {
|
for (int i = mTargets.size() - 1; i >= 0; --i) {
|
||||||
final WindowContainer target = mTargets.valueAt(i);
|
final WindowContainer target = mTargets.valueAt(i);
|
||||||
if (target.getParent() != null) {
|
if (target.getParent() != null) {
|
||||||
@@ -247,9 +245,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
// Ensure surfaceControls are re-parented back into the hierarchy.
|
// Ensure surfaceControls are re-parented back into the hierarchy.
|
||||||
t.reparent(targetLeash, origParent);
|
t.reparent(targetLeash, origParent);
|
||||||
t.setLayer(targetLeash, target.getLastLayer());
|
t.setLayer(targetLeash, target.getLastLayer());
|
||||||
// TODO(shell-transitions): Once all remotables have been moved, see if there is
|
|
||||||
// a more appropriate place to do the following. This may
|
|
||||||
// involve passing an SF transaction from shell on finish.
|
|
||||||
target.getRelativePosition(tmpPos);
|
target.getRelativePosition(tmpPos);
|
||||||
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
|
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
|
||||||
t.setCornerRadius(targetLeash, 0);
|
t.setCornerRadius(targetLeash, 0);
|
||||||
@@ -257,25 +252,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
displays.add(target.getDisplayContent());
|
displays.add(target.getDisplayContent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Need to update layers on ALL displays (for now) since they were all paused while
|
// Need to update layers on involved displays since they were all paused while
|
||||||
// the animation played.
|
// the animation played. This puts the layers back into the correct order.
|
||||||
for (int i = displays.size() - 1; i >= 0; --i) {
|
for (int i = displays.size() - 1; i >= 0; --i) {
|
||||||
if (displays.valueAt(i) == null) continue;
|
if (displays.valueAt(i) == null) continue;
|
||||||
displays.valueAt(i).assignChildLayers(t);
|
displays.valueAt(i).assignChildLayers(t);
|
||||||
}
|
}
|
||||||
// Also pro-actively hide going-invisible activity surfaces in same transaction to
|
if (rootLeash.isValid()) {
|
||||||
// prevent flickers due to reparenting and animation z-order mismatch.
|
t.reparent(rootLeash, null);
|
||||||
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);
|
|
||||||
|
/**
|
||||||
|
* The transition has finished animating and is ready to finalize WM state. This should not
|
||||||
|
* be called directly; use {@link TransitionController#finishTransition} instead.
|
||||||
|
*/
|
||||||
|
void finishTransition() {
|
||||||
|
mStartTransaction = mFinishTransaction = null;
|
||||||
|
if (mState < STATE_PLAYING) {
|
||||||
|
throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
|
||||||
}
|
}
|
||||||
mRootLeash = null;
|
|
||||||
t.apply();
|
|
||||||
t.close();
|
|
||||||
|
|
||||||
// Commit all going-invisible containers
|
// Commit all going-invisible containers
|
||||||
for (int i = 0; i < mParticipants.size(); ++i) {
|
for (int i = 0; i < mParticipants.size(); ++i) {
|
||||||
@@ -343,29 +339,60 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
// Resolve the animating targets from the participants
|
// Resolve the animating targets from the participants
|
||||||
mTargets = calculateTargets(mParticipants, mChanges);
|
mTargets = calculateTargets(mParticipants, mChanges);
|
||||||
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
|
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
|
||||||
mRootLeash = info.getRootLeash();
|
|
||||||
|
|
||||||
handleNonAppWindowsInTransition(displayId, mType, mFlags);
|
handleNonAppWindowsInTransition(displayId, mType, mFlags);
|
||||||
|
|
||||||
|
// Manually show any activities that are visibleRequested. This is needed to properly
|
||||||
|
// support simultaneous animation queueing/merging. Specifically, if transition A makes
|
||||||
|
// an activity invisible, it's finishTransaction (which is applied *after* the animation)
|
||||||
|
// will hide the activity surface. If transition B then makes the activity visible again,
|
||||||
|
// the normal surfaceplacement logic won't add a show to this start transaction because
|
||||||
|
// the activity visibility hasn't been committed yet. To deal with this, we have to manually
|
||||||
|
// show here in the same way that we manually hide in finishTransaction.
|
||||||
|
for (int i = mParticipants.size() - 1; i >= 0; --i) {
|
||||||
|
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
|
||||||
|
if (ar == null || !ar.mVisibleRequested) continue;
|
||||||
|
transaction.show(ar.getSurfaceControl());
|
||||||
|
}
|
||||||
|
|
||||||
|
mStartTransaction = transaction;
|
||||||
|
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
|
||||||
|
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
|
||||||
if (mController.getTransitionPlayer() != null) {
|
if (mController.getTransitionPlayer() != null) {
|
||||||
try {
|
try {
|
||||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
|
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
|
||||||
"Calling onTransitionReady: %s", info);
|
"Calling onTransitionReady: %s", info);
|
||||||
mController.getTransitionPlayer().onTransitionReady(this, info, transaction);
|
mController.getTransitionPlayer().onTransitionReady(
|
||||||
|
this, info, transaction, mFinishTransaction);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// If there's an exception when trying to send the mergedTransaction to the
|
// If there's an exception when trying to send the mergedTransaction to the
|
||||||
// client, we should finish and 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();
|
cleanUpOnFailure();
|
||||||
finishTransition();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No player registered, so just finish/apply immediately
|
// No player registered, so just finish/apply immediately
|
||||||
transaction.apply();
|
cleanUpOnFailure();
|
||||||
finishTransition();
|
|
||||||
}
|
}
|
||||||
mSyncId = -1;
|
mSyncId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the remote failed for any reason, use this to do any appropriate clean-up. Do not call
|
||||||
|
* this directly, it's designed to by called by {@link TransitionController} only.
|
||||||
|
*/
|
||||||
|
void cleanUpOnFailure() {
|
||||||
|
// No need to clean-up if this isn't playing yet.
|
||||||
|
if (mState < STATE_PLAYING) return;
|
||||||
|
|
||||||
|
if (mStartTransaction != null) {
|
||||||
|
mStartTransaction.apply();
|
||||||
|
}
|
||||||
|
if (mFinishTransaction != null) {
|
||||||
|
mFinishTransaction.apply();
|
||||||
|
}
|
||||||
|
finishTransition();
|
||||||
|
}
|
||||||
|
|
||||||
private void handleNonAppWindowsInTransition(int displayId,
|
private void handleNonAppWindowsInTransition(int displayId,
|
||||||
@WindowManager.TransitionType int transit, int flags) {
|
@WindowManager.TransitionType int transit, int flags) {
|
||||||
final DisplayContent dc =
|
final DisplayContent dc =
|
||||||
|
|||||||
@@ -42,17 +42,24 @@ class TransitionController {
|
|||||||
private static final String TAG = "TransitionController";
|
private static final String TAG = "TransitionController";
|
||||||
|
|
||||||
private ITransitionPlayer mTransitionPlayer;
|
private ITransitionPlayer mTransitionPlayer;
|
||||||
private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> mTransitionPlayer = null;
|
|
||||||
final ActivityTaskManagerService mAtm;
|
final ActivityTaskManagerService mAtm;
|
||||||
|
|
||||||
/** Currently playing transitions. When finished, records are removed from this list. */
|
/**
|
||||||
|
* Currently playing transitions (in the order they were started). When finished, records are
|
||||||
|
* removed from this list.
|
||||||
|
*/
|
||||||
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
|
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
|
||||||
* The transition currently being constructed (collecting participants).
|
// clean-up/finish any playing transitions.
|
||||||
* TODO(shell-transitions): When simultaneous transitions are supported, merge this with
|
for (int i = 0; i < mPlayingTransitions.size(); ++i) {
|
||||||
* mPlayingTransitions.
|
mPlayingTransitions.get(i).cleanUpOnFailure();
|
||||||
*/
|
}
|
||||||
|
mPlayingTransitions.clear();
|
||||||
|
mTransitionPlayer = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The transition currently being constructed (collecting participants). */
|
||||||
private Transition mCollectingTransition = null;
|
private Transition mCollectingTransition = null;
|
||||||
|
|
||||||
TransitionController(ActivityTaskManagerService atm) {
|
TransitionController(ActivityTaskManagerService atm) {
|
||||||
|
|||||||
@@ -1428,7 +1428,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo,
|
public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo,
|
||||||
SurfaceControl.Transaction transaction) throws RemoteException {
|
SurfaceControl.Transaction transaction, SurfaceControl.Transaction finishT)
|
||||||
|
throws RemoteException {
|
||||||
mLastTransit = Transition.fromBinder(transitToken);
|
mLastTransit = Transition.fromBinder(transitToken);
|
||||||
mLastReady = transitionInfo;
|
mLastReady = transitionInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user