Merge "Keep only one back animation playing." into udc-dev

This commit is contained in:
Wei Sheng Shih
2023-03-30 03:47:25 +00:00
committed by Android (Google) Code Review
4 changed files with 134 additions and 111 deletions

View File

@@ -258,7 +258,7 @@ public class AppTransitionController {
tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
if (mDisplayContent.mAtmService.mBackNavigationController
.removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations(null);
mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations();
}
}

View File

@@ -62,13 +62,12 @@ import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Controller to handle actions related to the back gesture on the server side.
*/
class BackNavigationController {
private static final String TAG = "BackNavigationController";
private static final String TAG = "CoreBackPreview";
private WindowManagerService mWindowManagerService;
private boolean mBackAnimationInProgress;
private @BackNavigationInfo.BackTargetType int mLastBackType;
@@ -76,7 +75,13 @@ class BackNavigationController {
private Runnable mPendingAnimation;
private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
AnimationHandler mAnimationHandler;
private AnimationHandler mAnimationHandler;
/**
* The transition who match the back navigation targets,
* release animation after this transition finish.
*/
private Transition mWaitTransitionFinish;
private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>();
@@ -140,6 +145,11 @@ class BackNavigationController {
BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder();
synchronized (wmService.mGlobalLock) {
if (isMonitoringTransition()) {
Slog.w(TAG, "Previous animation hasn't finish, status: " + mAnimationHandler);
// Don't start any animation for it.
return null;
}
WindowManagerInternal windowManagerInternal =
LocalServices.getService(WindowManagerInternal.class);
IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
@@ -374,7 +384,7 @@ class BackNavigationController {
}
boolean isMonitoringTransition() {
return isWaitBackTransition() || mNavigationMonitor.isMonitoring();
return mAnimationHandler.mComposed || mNavigationMonitor.isMonitorForRemote();
}
private void scheduleAnimation(@NonNull AnimationHandler.ScheduleAnimationBuilder builder) {
@@ -477,7 +487,7 @@ class BackNavigationController {
return false;
}
private static class NavigationMonitor {
private class NavigationMonitor {
// The window which triggering the back navigation.
private WindowState mNavigatingWindow;
private RemoteCallback mObserver;
@@ -487,15 +497,23 @@ class BackNavigationController {
mObserver = observer;
}
void stopMonitor() {
mNavigatingWindow = null;
void stopMonitorForRemote() {
mObserver = null;
}
boolean isMonitoring() {
void stopMonitorTransition() {
mNavigatingWindow = null;
}
boolean isMonitorForRemote() {
return mNavigatingWindow != null && mObserver != null;
}
boolean isMonitorAnimationOrTransition() {
return mNavigatingWindow != null
&& (mAnimationHandler.mComposed || mAnimationHandler.mWaitTransition);
}
/**
* Notify focus window changed during back navigation. This will cancel the gesture for
* scenarios like: a system window popup, or when an activity add a new window.
@@ -506,7 +524,8 @@ class BackNavigationController {
* a short time, but we should not cancel the navigation.
*/
private void onFocusWindowChanged(WindowState newFocus) {
if (!isMonitoring() || !atSameDisplay(newFocus)) {
if (!atSameDisplay(newFocus)
|| !(isMonitorForRemote() || isMonitorAnimationOrTransition())) {
return;
}
// Keep navigating if either new focus == navigating window or null.
@@ -514,7 +533,13 @@ class BackNavigationController {
&& (newFocus.mActivityRecord == null
|| (newFocus.mActivityRecord == mNavigatingWindow.mActivityRecord))) {
EventLogTags.writeWmBackNaviCanceled("focusWindowChanged");
mObserver.sendResult(null /* result */);
if (isMonitorForRemote()) {
mObserver.sendResult(null /* result */);
}
if (isMonitorAnimationOrTransition()) {
// transition won't happen, cancel internal status
clearBackAnimations();
}
}
}
@@ -523,7 +548,7 @@ class BackNavigationController {
*/
private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening,
ArrayList<WindowContainer> closing) {
if (!isMonitoring()) {
if (!isMonitorForRemote() && !isMonitorAnimationOrTransition()) {
return;
}
final ArrayList<WindowContainer> all = new ArrayList<>(opening);
@@ -531,7 +556,12 @@ class BackNavigationController {
for (WindowContainer app : all) {
if (app.hasChild(mNavigatingWindow)) {
EventLogTags.writeWmBackNaviCanceled("transitionHappens");
mObserver.sendResult(null /* result */);
if (isMonitorForRemote()) {
mObserver.sendResult(null /* result */);
}
if (isMonitorAnimationOrTransition()) {
clearBackAnimations();
}
break;
}
}
@@ -539,6 +569,9 @@ class BackNavigationController {
}
private boolean atSameDisplay(WindowState newFocus) {
if (mNavigatingWindow == null) {
return false;
}
final int navigatingDisplayId = mNavigatingWindow.getDisplayId();
return newFocus == null || newFocus.getDisplayId() == navigatingDisplayId;
}
@@ -546,17 +579,15 @@ class BackNavigationController {
// For shell transition
/**
* Check whether the transition targets was animated by back gesture animation.
* Because the opening target could request to do other stuff at onResume, so it could become
* close target for a transition. So the condition here is
* The closing target should only exist in close list, but the opening target can be either in
* open or close list.
* @return {@code true} if the participants of this transition was animated by back gesture
* animations, and shouldn't join next transition.
* Check whether the transition targets was animated by back gesture animation.
* Because the opening target could request to do other stuff at onResume, so it could become
* close target for a transition. So the condition here is
* The closing target should only exist in close list, but the opening target can be either in
* open or close list.
*/
boolean containsBackAnimationTargets(Transition transition) {
void onTransactionReady(Transition transition) {
if (!isMonitoringTransition()) {
return false;
return;
}
final ArraySet<WindowContainer> targets = transition.mParticipants;
for (int i = targets.size() - 1; i >= 0; --i) {
@@ -576,33 +607,44 @@ class BackNavigationController {
&& mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
if (!matchAnimationTargets) {
mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
} else {
if (mWaitTransitionFinish != null) {
Slog.e(TAG, "Gesture animation is applied on another transition?");
}
mWaitTransitionFinish = transition;
}
mTmpOpenApps.clear();
mTmpCloseApps.clear();
return matchAnimationTargets;
}
boolean isMonitorTransitionTarget(WindowContainer wc) {
if (!isWaitBackTransition()) {
if (!isWaitBackTransition() || mWaitTransitionFinish == null) {
return false;
}
return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
}
/**
* Cleanup animation, this can either happen when transition ready or finish.
* @param cleanupTransaction The transaction which the caller want to apply the internal
* cleanup together.
* Cleanup animation, this can either happen when legacy transition ready, or when the Shell
* transition finish.
*/
void clearBackAnimations(SurfaceControl.Transaction cleanupTransaction) {
mAnimationHandler.clearBackAnimateTarget(cleanupTransaction);
void clearBackAnimations() {
mAnimationHandler.clearBackAnimateTarget();
mNavigationMonitor.stopMonitorTransition();
mWaitTransitionFinish = null;
}
/**
/**
* Called when a transition finished.
* Handle the pending animation when the running transition finished.
* @param targets The final animation targets derived in transition.
*/
boolean handleDeferredBackAnimation(@NonNull ArrayList<Transition.ChangeInfo> targets) {
* @param finishedTransition The finished transition target.
*/
boolean onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
@NonNull Transition finishedTransition) {
if (finishedTransition == mWaitTransitionFinish) {
clearBackAnimations();
}
if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) {
return false;
}
@@ -660,7 +702,7 @@ class BackNavigationController {
private boolean mComposed;
private boolean mWaitTransition;
private int mSwitchType = UNKNOWN;
private SurfaceControl.Transaction mFinishedTransaction;
// This will be set before transition happen, to know whether the real opening target
// exactly match animating target. When target match, reparent the starting surface to
// the opening target like starting window do.
@@ -669,6 +711,7 @@ class BackNavigationController {
// request one during animating.
private int mRequestedStartingSurfaceTaskId;
private SurfaceControl mStartingSurface;
private ActivityRecord mOpenActivity;
AnimationHandler(WindowManagerService wms) {
mWindowManagerService = wms;
@@ -697,7 +740,8 @@ class BackNavigationController {
return true;
}
private void initiate(WindowContainer close, WindowContainer open) {
private void initiate(WindowContainer close, WindowContainer open,
ActivityRecord openActivity) {
WindowContainer closeTarget;
if (isActivitySwitch(close, open)) {
mSwitchType = ACTIVITY_SWITCH;
@@ -712,22 +756,26 @@ class BackNavigationController {
mCloseAdaptor = createAdaptor(closeTarget, false /* isOpen */);
mOpenAdaptor = createAdaptor(open, true /* isOpen */);
mOpenActivity = openActivity;
if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) {
Slog.w(TAG, "composeNewAnimations fail, skip");
clearBackAnimateTarget(null /* cleanupTransaction */);
clearBackAnimateTarget();
}
}
private boolean composeAnimations(@NonNull WindowContainer close,
@NonNull WindowContainer open) {
clearBackAnimateTarget(null /* cleanupTransaction */);
if (close == null || open == null) {
boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open,
ActivityRecord openActivity) {
if (mComposed || mWaitTransition) {
Slog.e(TAG, "Previous animation is running " + this);
return false;
}
clearBackAnimateTarget();
if (close == null || open == null || openActivity == null) {
Slog.e(TAG, "reset animation with null target close: "
+ close + " open: " + open);
return false;
}
initiate(close, open);
initiate(close, open, openActivity);
if (mSwitchType == UNKNOWN) {
return false;
}
@@ -791,24 +839,10 @@ class BackNavigationController {
return false;
}
boolean setFinishTransaction(SurfaceControl.Transaction finishTransaction) {
if (!mComposed) {
return false;
}
mFinishedTransaction = finishTransaction;
return true;
}
void finishPresentAnimations(SurfaceControl.Transaction t) {
void finishPresentAnimations() {
if (!mComposed) {
return;
}
final SurfaceControl.Transaction pt = t != null ? t
: mOpenAdaptor.mTarget.getPendingTransaction();
if (mFinishedTransaction != null) {
pt.merge(mFinishedTransaction);
mFinishedTransaction = null;
}
cleanUpWindowlessSurface();
if (mCloseAdaptor != null) {
@@ -819,6 +853,9 @@ class BackNavigationController {
mOpenAdaptor.mTarget.cancelAnimation();
mOpenAdaptor = null;
}
if (mOpenActivity != null && mOpenActivity.mLaunchTaskBehind) {
restoreLaunchBehind(mOpenActivity);
}
}
private void cleanUpWindowlessSurface() {
@@ -845,22 +882,14 @@ class BackNavigationController {
}
}
void clearBackAnimateTarget(SurfaceControl.Transaction cleanupTransaction) {
finishPresentAnimations(cleanupTransaction);
void clearBackAnimateTarget() {
finishPresentAnimations();
mComposed = false;
mWaitTransition = false;
mOpenTransitionTargetMatch = false;
mRequestedStartingSurfaceTaskId = 0;
mSwitchType = UNKNOWN;
if (mFinishedTransaction != null) {
Slog.w(TAG, "Clear back animation, found un-processed finished transaction");
if (cleanupTransaction != null) {
cleanupTransaction.merge(mFinishedTransaction);
} else {
mFinishedTransaction.apply();
}
mFinishedTransaction = null;
}
mOpenActivity = null;
}
// The close target must in close list
@@ -876,9 +905,9 @@ class BackNavigationController {
public String toString() {
return "AnimationTargets{"
+ " openTarget= "
+ mOpenAdaptor.mTarget
+ (mOpenAdaptor != null ? mOpenAdaptor.mTarget : "null")
+ " closeTarget= "
+ mCloseAdaptor.mTarget
+ (mCloseAdaptor != null ? mCloseAdaptor.mTarget : "null")
+ " mSwitchType= "
+ mSwitchType
+ " mComposed= "
@@ -1048,14 +1077,13 @@ class BackNavigationController {
* @return If the preview strategy is launch behind, returns the Activity that has
* launchBehind set, or null otherwise.
*/
private ActivityRecord applyPreviewStrategy(WindowContainer open,
private void applyPreviewStrategy(WindowContainer open,
ActivityRecord visibleOpenActivity) {
if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
createStartingSurface(getSnapshot(open));
return null;
return;
}
setLaunchBehind(visibleOpenActivity);
return visibleOpenActivity;
}
Runnable build() {
@@ -1071,19 +1099,12 @@ class BackNavigationController {
return null;
}
if (!composeAnimations(mCloseTarget, mOpenTarget)) {
if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) {
return null;
}
final ActivityRecord launchBehindActivity =
applyPreviewStrategy(mOpenTarget, openActivity);
applyPreviewStrategy(mOpenTarget, openActivity);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(
launchBehindActivity != null ? triggerBack -> {
if (!triggerBack) {
restoreLaunchBehind(launchBehindActivity);
}
} : null,
mCloseTarget);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
final RemoteAnimationTarget[] targets = getAnimationTargets();
return () -> {
@@ -1096,31 +1117,17 @@ class BackNavigationController {
};
}
private IBackAnimationFinishedCallback makeAnimationFinishedCallback(
Consumer<Boolean> b, WindowContainer closeTarget) {
private IBackAnimationFinishedCallback makeAnimationFinishedCallback() {
return new IBackAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished(boolean triggerBack) {
final SurfaceControl.Transaction finishedTransaction =
new SurfaceControl.Transaction();
synchronized (mWindowManagerService.mGlobalLock) {
if (b != null) {
b.accept(triggerBack);
}
if (triggerBack) {
final SurfaceControl surfaceControl =
closeTarget.getSurfaceControl();
if (surfaceControl != null && surfaceControl.isValid()) {
// Hide the close target surface when transition start.
finishedTransaction.hide(surfaceControl);
}
}
if (!setFinishTransaction(finishedTransaction)) {
finishedTransaction.apply();
if (!mComposed) {
// animation was canceled
return;
}
if (!triggerBack) {
clearBackAnimateTarget(
null /* cleanupTransaction */);
clearBackAnimateTarget();
} else {
mWaitTransition = true;
}
@@ -1180,6 +1187,14 @@ class BackNavigationController {
}
void startAnimation() {
if (!mBackAnimationInProgress) {
// gesture is already finished, do not start animation
if (mPendingAnimation != null) {
clearBackAnimations();
mPendingAnimation = null;
}
return;
}
if (mPendingAnimation != null) {
mPendingAnimation.run();
mPendingAnimation = null;
@@ -1192,7 +1207,7 @@ class BackNavigationController {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
+ "triggerBack=%b", backType, triggerBack);
mNavigationMonitor.stopMonitor();
mNavigationMonitor.stopMonitorForRemote();
mBackAnimationInProgress = false;
mShowWallpaper = false;
mPendingAnimationBuilder = null;

View File

@@ -1031,7 +1031,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mTmpTransaction.apply();
// Handle back animation if it's already started.
mController.mAtm.mBackNavigationController.handleDeferredBackAnimation(mTargets);
mController.mAtm.mBackNavigationController.onTransitionFinish(mTargets, this);
mController.mFinishingTransition = null;
}
@@ -1136,8 +1136,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
}
// Check whether the participants were animated from back navigation.
final boolean markBackAnimated = mController.mAtm.mBackNavigationController
.containsBackAnimationTargets(this);
mController.mAtm.mBackNavigationController.onTransactionReady(this);
// Resolve the animating targets from the participants.
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
@@ -1150,9 +1149,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mTargetDisplays.add(dc);
}
if (markBackAnimated) {
mController.mAtm.mBackNavigationController.clearBackAnimations(mStartTransaction);
}
if (mOverrideOptions != null) {
info.setAnimationOptions(mOverrideOptions);
if (mOverrideOptions.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) {

View File

@@ -89,11 +89,12 @@ public class BackNavigationControllerTests extends WindowTestsBase {
@Before
public void setUp() throws Exception {
mBackNavigationController = Mockito.spy(new BackNavigationController());
final BackNavigationController original = new BackNavigationController();
original.setWindowManager(mWm);
mBackNavigationController = Mockito.spy(original);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
mWindowManagerInternal = mock(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
mBackNavigationController.setWindowManager(mWm);
mBackAnimationAdapter = mock(BackAnimationAdapter.class);
mRootHomeTask = initHomeActivity();
}
@@ -129,7 +130,9 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// verify if back animation would start.
assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation());
// reset drawning status
// reset drawing status
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
topTask.forAllWindows(w -> {
makeWindowVisibleAndDrawn(w);
}, true);
@@ -138,6 +141,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
doReturn(true).when(recordA).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -194,6 +199,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation());
// reset drawing status
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
testCase.recordFront.forAllWindows(w -> {
makeWindowVisibleAndDrawn(w);
}, true);
@@ -202,6 +209,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
doReturn(true).when(testCase.recordBack).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -240,6 +249,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -553,6 +564,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertTrue(toHomeBuilder.mIsLaunchBehind);
toHomeBuilder.build();
verify(animationHandler, never()).createStartingSurface(any());
animationHandler.clearBackAnimateTarget();
// Back to ACTIVITY and TASK have the same logic, just with different target.
final ActivityRecord topActivity = createActivityRecord(task);