End transitions when receiving merge request
The default behavior (if nothing is done) for a merge request
is to let the animation continue and queue up the incoming animation
to start once the current one has finished.
This is maybe the "smoothest" default; however, it is not
responsive for the user. So, instead, go through and, where
appropriate, make animations "jump-to-end" when they receive
a merge request. This way the user will see the new animation
start immediately after they perform an action. There is
technically a "jump" in the animation, but it seems like the
responsiveness is preferable and the jumps aren't super
noticable anyways. This is also how legacy transitions works
so it's not a behavior change.
Bug: 236309064
Test: interrupt animations with other transitions and observe.
Example: launch an app and then go "back" before it finishes
animating.
Change-Id: I4a76e7efe2c610b22f3fe97b0bd3ab692a01be08
This commit is contained in:
@@ -42,6 +42,7 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SP
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
|
||||
import static com.android.wm.shell.transition.Transitions.isOpeningType;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.Context;
|
||||
@@ -248,6 +249,13 @@ public class PipTransition extends PipTransitionController {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
end();
|
||||
}
|
||||
|
||||
/** Helper to identify whether this handler is currently the one playing an animation */
|
||||
private boolean isAnimatingLocally() {
|
||||
return mFinishTransaction != null;
|
||||
@@ -282,6 +290,13 @@ public class PipTransition extends PipTransitionController {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
Animator animator = mPipAnimationController.getCurrentAnimator();
|
||||
if (animator == null) return;
|
||||
animator.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleRotateDisplay(int startRotation, int endRotation,
|
||||
WindowContainerTransaction wct) {
|
||||
|
||||
@@ -237,6 +237,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH
|
||||
@NonNull final Transitions.TransitionFinishCallback finishCallback) {
|
||||
}
|
||||
|
||||
/** End the currently-playing PiP animation. */
|
||||
public void end() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface for PiP transitions (both from and to PiP mode)
|
||||
*/
|
||||
|
||||
@@ -225,11 +225,27 @@ class SplitScreenTransitions {
|
||||
|
||||
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
|
||||
IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
|
||||
if (mergeTarget == mAnimatingTransition && mActiveRemoteHandler != null) {
|
||||
if (mergeTarget != mAnimatingTransition) return;
|
||||
if (mActiveRemoteHandler != null) {
|
||||
mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
|
||||
} else {
|
||||
for (int i = mAnimations.size() - 1; i >= 0; --i) {
|
||||
final Animator anim = mAnimations.get(i);
|
||||
mTransitions.getAnimExecutor().execute(anim::end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean end() {
|
||||
// If its remote, there's nothing we can do right now.
|
||||
if (mActiveRemoteHandler != null) return false;
|
||||
for (int i = mAnimations.size() - 1; i >= 0; --i) {
|
||||
final Animator anim = mAnimations.get(i);
|
||||
mTransitions.getAnimExecutor().execute(anim::end);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void onTransitionMerged(@NonNull IBinder transition) {
|
||||
// Once a pending enter transition got merged, make sure to append the reset of finishing
|
||||
// operations to the finish transition.
|
||||
|
||||
@@ -1521,6 +1521,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
|
||||
}
|
||||
|
||||
/** Jump the current transition animation to the end. */
|
||||
public boolean end() {
|
||||
return mSplitTransitions.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionMerged(@NonNull IBinder transition) {
|
||||
mSplitTransitions.onTransitionMerged(transition);
|
||||
|
||||
@@ -53,10 +53,18 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
private static class MixedTransition {
|
||||
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
|
||||
|
||||
/** The default animation for this mixed transition. */
|
||||
static final int ANIM_TYPE_DEFAULT = 0;
|
||||
|
||||
/** For ENTER_PIP_FROM_SPLIT, indicates that this is a to-home animation. */
|
||||
static final int ANIM_TYPE_GOING_HOME = 1;
|
||||
|
||||
final int mType;
|
||||
int mAnimType = 0;
|
||||
final IBinder mTransition;
|
||||
|
||||
Transitions.TransitionFinishCallback mFinishCallback = null;
|
||||
Transitions.TransitionHandler mLeftoversHandler = null;
|
||||
|
||||
/**
|
||||
* Mixed transitions are made up of multiple "parts". This keeps track of how many
|
||||
@@ -128,7 +136,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
MixedTransition mixed = null;
|
||||
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
|
||||
if (mActiveTransitions.get(i).mTransition != transition) continue;
|
||||
mixed = mActiveTransitions.remove(i);
|
||||
mixed = mActiveTransitions.get(i);
|
||||
break;
|
||||
}
|
||||
if (mixed == null) return false;
|
||||
@@ -137,6 +145,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
|
||||
finishCallback);
|
||||
} else {
|
||||
mActiveTransitions.remove(mixed);
|
||||
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
|
||||
+ mixed.mType);
|
||||
}
|
||||
@@ -178,6 +187,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
|
||||
--mixed.mInFlightSubAnimations;
|
||||
if (mixed.mInFlightSubAnimations > 0) return;
|
||||
mActiveTransitions.remove(mixed);
|
||||
if (isGoingHome) {
|
||||
mSplitHandler.onTransitionAnimationComplete();
|
||||
}
|
||||
@@ -216,8 +226,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
finishCB);
|
||||
// Dispatch the rest of the transition normally. This will most-likely be taken by
|
||||
// recents or default handler.
|
||||
mPlayer.dispatchTransition(mixed.mTransition, everythingElse, otherStartT,
|
||||
finishTransaction, finishCB, this);
|
||||
mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, everythingElse,
|
||||
otherStartT, finishTransaction, finishCB, this);
|
||||
} else {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just "
|
||||
+ "forward animation to Pip-Handler.");
|
||||
@@ -235,6 +245,32 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
for (int i = 0; i < mActiveTransitions.size(); ++i) {
|
||||
if (mActiveTransitions.get(i) != mergeTarget) continue;
|
||||
MixedTransition mixed = mActiveTransitions.get(i);
|
||||
if (mixed.mInFlightSubAnimations <= 0) {
|
||||
// Already done, so no need to end it.
|
||||
return;
|
||||
}
|
||||
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
|
||||
if (mixed.mAnimType == MixedTransition.ANIM_TYPE_GOING_HOME) {
|
||||
boolean ended = mSplitHandler.end();
|
||||
// If split couldn't end (because it is remote), then don't end everything else
|
||||
// since we have to play out the animation anyways.
|
||||
if (!ended) return;
|
||||
mPipHandler.end();
|
||||
if (mixed.mLeftoversHandler != null) {
|
||||
mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
|
||||
finishCallback);
|
||||
}
|
||||
} else {
|
||||
mPipHandler.end();
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Playing a mixed transition with unknown type? "
|
||||
+ mixed.mType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -523,6 +523,18 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
ArrayList<Animator> anims = mAnimations.get(mergeTarget);
|
||||
if (anims == null) return;
|
||||
for (int i = anims.size() - 1; i >= 0; --i) {
|
||||
final Animator anim = anims.get(i);
|
||||
mAnimExecutor.execute(anim::end);
|
||||
}
|
||||
}
|
||||
|
||||
private void edgeExtendWindow(TransitionInfo.Change change,
|
||||
Animation a, SurfaceControl.Transaction startTransaction,
|
||||
SurfaceControl.Transaction finishTransaction) {
|
||||
|
||||
@@ -114,6 +114,8 @@ public class RemoteAnimationAdapterCompat {
|
||||
private static IRemoteTransition.Stub wrapRemoteTransition(
|
||||
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
|
||||
return new IRemoteTransition.Stub() {
|
||||
final ArrayMap<IBinder, Runnable> mFinishRunnables = new ArrayMap<>();
|
||||
|
||||
@Override
|
||||
public void startAnimation(IBinder token, TransitionInfo info,
|
||||
SurfaceControl.Transaction t,
|
||||
@@ -229,19 +231,32 @@ public class RemoteAnimationAdapterCompat {
|
||||
}
|
||||
}
|
||||
};
|
||||
synchronized (mFinishRunnables) {
|
||||
mFinishRunnables.put(token, animationFinishedCallback);
|
||||
}
|
||||
// TODO(bc-unlcok): Pass correct transit type.
|
||||
remoteAnimationAdapter.onAnimationStart(
|
||||
TRANSIT_OLD_NONE,
|
||||
appsCompat, wallpapersCompat, nonAppsCompat,
|
||||
animationFinishedCallback);
|
||||
remoteAnimationAdapter.onAnimationStart(TRANSIT_OLD_NONE,
|
||||
appsCompat, wallpapersCompat, nonAppsCompat, () -> {
|
||||
synchronized (mFinishRunnables) {
|
||||
if (mFinishRunnables.remove(token) == null) return;
|
||||
}
|
||||
animationFinishedCallback.run();
|
||||
});
|
||||
}
|
||||
|
||||
@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.
|
||||
// TODO: hook up merge to recents onTaskAppeared if applicable. Until then, adapt
|
||||
// to legacy cancel.
|
||||
final Runnable finishRunnable;
|
||||
synchronized (mFinishRunnables) {
|
||||
finishRunnable = mFinishRunnables.remove(mergeTarget);
|
||||
}
|
||||
if (finishRunnable == null) return;
|
||||
remoteAnimationAdapter.onAnimationCancelled();
|
||||
finishRunnable.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
@@ -191,6 +192,9 @@ public class KeyguardService extends Service {
|
||||
|
||||
private static IRemoteTransition wrap(IRemoteAnimationRunner runner) {
|
||||
return new IRemoteTransition.Stub() {
|
||||
final ArrayMap<IBinder, IRemoteTransitionFinishedCallback> mFinishCallbacks =
|
||||
new ArrayMap<>();
|
||||
|
||||
@Override
|
||||
public void startAnimation(IBinder transition, TransitionInfo info,
|
||||
SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
|
||||
@@ -205,11 +209,17 @@ public class KeyguardService extends Service {
|
||||
t.setAlpha(change.getLeash(), 1.0f);
|
||||
}
|
||||
t.apply();
|
||||
synchronized (mFinishCallbacks) {
|
||||
mFinishCallbacks.put(transition, finishCallback);
|
||||
}
|
||||
runner.onAnimationStart(getTransitionOldType(info.getType(), info.getFlags(), apps),
|
||||
apps, wallpapers, nonApps,
|
||||
new IRemoteAnimationFinishedCallback.Stub() {
|
||||
@Override
|
||||
public void onAnimationFinished() throws RemoteException {
|
||||
synchronized (mFinishCallbacks) {
|
||||
if (mFinishCallbacks.remove(transition) == null) return;
|
||||
}
|
||||
Slog.d(TAG, "Finish IRemoteAnimationRunner.");
|
||||
finishCallback.onTransitionFinished(null /* wct */, null /* t */);
|
||||
}
|
||||
@@ -220,7 +230,20 @@ public class KeyguardService extends Service {
|
||||
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||
IRemoteTransitionFinishedCallback finishCallback) {
|
||||
|
||||
try {
|
||||
final IRemoteTransitionFinishedCallback origFinishCB;
|
||||
synchronized (mFinishCallbacks) {
|
||||
origFinishCB = mFinishCallbacks.remove(transition);
|
||||
}
|
||||
if (origFinishCB == null) {
|
||||
// already finished (or not started yet), so do nothing.
|
||||
return;
|
||||
}
|
||||
runner.onAnimationCancelled();
|
||||
origFinishCB.onTransitionFinished(null /* wct */, null /* t */);
|
||||
} catch (RemoteException e) {
|
||||
// nothing, we'll just let it finish on its own I guess.
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user