From 5c85e5b6c09b2a60b14d3c74ee9bd01c7d338f35 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Wed, 17 Aug 2022 06:10:37 +0000 Subject: [PATCH] Migrate back animation to shell transition When back animation finished, it will invoke the real back callback and cause a new transition started. In this CL, we introduce the back transition handler to consume the incoming transition request and takeover the whole transition if the transition info contians same departing window token. This also seperated the behaviors of enabled/disabled shell transition. Bug: 238475694 Test: Enabled shell transition, atest BackNavigationControllerTests BackAnimationControllerTest Change-Id: I57e7c89ce6cb7a99ab3af403704b9dd948f26151 --- .../android/window/BackNavigationInfo.java | 36 ++++++++- .../shell/back/BackAnimationController.java | 70 +++++++++++++---- .../wm/shell/back/BackTransitionHandler.java | 78 +++++++++++++++++++ .../wm/shell/dagger/WMShellBaseModule.java | 5 +- .../back/BackAnimationControllerTest.java | 12 ++- .../server/wm/BackNavigationController.java | 50 +++++++++--- 6 files changed, 218 insertions(+), 33 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index 87cfbb2a57055..9b91cf2e9db62 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -89,6 +89,8 @@ public final class BackNavigationInfo implements Parcelable { @Nullable private final IOnBackInvokedCallback mOnBackInvokedCallback; private final boolean mPrepareRemoteAnimation; + @Nullable + private WindowContainerToken mDepartingWindowContainerToken; /** * Create a new {@link BackNavigationInfo} instance. @@ -97,6 +99,7 @@ public final class BackNavigationInfo implements Parcelable { * @param onBackNavigationDone The callback to be called once the client is done with the * back preview. * @param onBackInvokedCallback The back callback registered by the current top level window. + * @param departingWindowContainerToken The {@link WindowContainerToken} of departing window. * @param isPrepareRemoteAnimation Return whether the core is preparing a back gesture * animation, if true, the caller of startBackNavigation should * be expected to receive an animation start callback. @@ -104,11 +107,13 @@ public final class BackNavigationInfo implements Parcelable { private BackNavigationInfo(@BackTargetType int type, @Nullable RemoteCallback onBackNavigationDone, @Nullable IOnBackInvokedCallback onBackInvokedCallback, - boolean isPrepareRemoteAnimation) { + boolean isPrepareRemoteAnimation, + @Nullable WindowContainerToken departingWindowContainerToken) { mType = type; mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; mPrepareRemoteAnimation = isPrepareRemoteAnimation; + mDepartingWindowContainerToken = departingWindowContainerToken; } private BackNavigationInfo(@NonNull Parcel in) { @@ -116,6 +121,7 @@ public final class BackNavigationInfo implements Parcelable { mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR); mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); mPrepareRemoteAnimation = in.readBoolean(); + mDepartingWindowContainerToken = in.readTypedObject(WindowContainerToken.CREATOR); } @Override @@ -124,6 +130,7 @@ public final class BackNavigationInfo implements Parcelable { dest.writeTypedObject(mOnBackNavigationDone, flags); dest.writeStrongInterface(mOnBackInvokedCallback); dest.writeBoolean(mPrepareRemoteAnimation); + dest.writeTypedObject(mDepartingWindowContainerToken, flags); } /** @@ -156,6 +163,18 @@ public final class BackNavigationInfo implements Parcelable { return mPrepareRemoteAnimation; } + /** + * Returns the {@link WindowContainerToken} of the highest container in the hierarchy being + * removed. + *

+ * For example, if an Activity is the last one of its Task, the Task's token will be given. + * Otherwise, it will be the Activity's token. + */ + @Nullable + public WindowContainerToken getDepartingWindowContainerToken() { + return mDepartingWindowContainerToken; + } + /** * Callback to be called when the back preview is finished in order to notify the server that * it can clean up the resources created for the animation. @@ -193,6 +212,7 @@ public final class BackNavigationInfo implements Parcelable { + "mType=" + typeToString(mType) + " (" + mType + ")" + ", mOnBackNavigationDone=" + mOnBackNavigationDone + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback + + ", mWindowContainerToken=" + mDepartingWindowContainerToken + '}'; } @@ -228,6 +248,9 @@ public final class BackNavigationInfo implements Parcelable { @Nullable private IOnBackInvokedCallback mOnBackInvokedCallback = null; private boolean mPrepareRemoteAnimation; + @Nullable + private WindowContainerToken mDepartingWindowContainerToken = null; + /** * @see BackNavigationInfo#getType() */ @@ -261,12 +284,21 @@ public final class BackNavigationInfo implements Parcelable { return this; } + /** + * @see BackNavigationInfo#getDepartingWindowContainerToken() + */ + public void setDepartingWCT(@NonNull WindowContainerToken windowContainerToken) { + mDepartingWindowContainerToken = windowContainerToken; + } + /** * Builds and returns an instance of {@link BackNavigationInfo} */ public BackNavigationInfo build() { return new BackNavigationInfo(mType, mOnBackNavigationDone, - mOnBackInvokedCallback, mPrepareRemoteAnimation); + mOnBackInvokedCallback, + mPrepareRemoteAnimation, + mDepartingWindowContainerToken); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index cd77802fa10bb..6f9c8b18625f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -58,6 +58,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; import java.util.concurrent.atomic.AtomicBoolean; @@ -75,6 +76,9 @@ public class BackAnimationController implements RemoteCallable mAnimationDefinition = new SparseArray<>(); + private final Transitions mTransitions; + private BackTransitionHandler mBackTransitionHandler; @VisibleForTesting final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() { @@ -184,9 +190,11 @@ public class BackAnimationController implements RemoteCallable { + finishBackNavigation(); + finishCallback.onTransitionFinished(null, null); + }); + } + private void createAdapter() { IBackAnimationRunner runner = new IBackAnimationRunner.Stub() { @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java new file mode 100644 index 0000000000000..6d72d9c1f6375 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackTransitionHandler.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.back; + +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wm.shell.transition.Transitions; + +class BackTransitionHandler implements Transitions.TransitionHandler { + private BackAnimationController mBackAnimationController; + private WindowContainerToken mDepartingWindowContainerToken; + + BackTransitionHandler(@NonNull BackAnimationController backAnimationController) { + mBackAnimationController = backAnimationController; + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mDepartingWindowContainerToken != null) { + final TransitionInfo.Change change = info.getChange(mDepartingWindowContainerToken); + if (change == null) { + return false; + } + + startTransaction.hide(change.getLeash()); + startTransaction.apply(); + mDepartingWindowContainerToken = null; + mBackAnimationController.finishTransition(finishCallback); + return true; + } + + return false; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + return null; + } + + @Override + public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + } + + void setDepartingWindowContainerToken( + @Nullable WindowContainerToken departingWindowContainerToken) { + mDepartingWindowContainerToken = departingWindowContainerToken; + } +} + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 80cdd1f79cb5d..2932bf1de4f61 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -273,12 +273,13 @@ public abstract class WMShellBaseModule { Context context, ShellInit shellInit, @ShellMainThread ShellExecutor shellExecutor, - @ShellBackgroundThread Handler backgroundHandler + @ShellBackgroundThread Handler backgroundHandler, + Transitions transitions ) { if (BackAnimationController.IS_ENABLED) { return Optional.of( new BackAnimationController(shellInit, shellExecutor, backgroundHandler, - context)); + context, transitions)); } return Optional.empty(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 9f39598e70a52..ac5236f95040d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -61,6 +61,7 @@ import com.android.internal.util.test.FakeSettingsProvider; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; import org.junit.Before; import org.junit.Rule; @@ -98,6 +99,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Mock private IRemoteAnimationRunner mBackAnimationRunner; + @Mock + private Transitions mTransitions; + private BackAnimationController mController; private int mEventTime = 0; @@ -117,7 +121,7 @@ public class BackAnimationControllerTest extends ShellTestCase { mController = new BackAnimationController(mShellInit, mShellExecutor, new Handler(mTestableLooper.getLooper()), mActivityTaskManager, mContext, - mContentResolver); + mContentResolver, mTransitions); mShellInit.init(); mEventTime = 0; mShellExecutor.flushAll(); @@ -209,7 +213,7 @@ public class BackAnimationControllerTest extends ShellTestCase { mController = new BackAnimationController(shellInit, mShellExecutor, new Handler(mTestableLooper.getLooper()), mActivityTaskManager, mContext, - mContentResolver); + mContentResolver, mTransitions); shellInit.init(); mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner); @@ -250,6 +254,8 @@ public class BackAnimationControllerTest extends ShellTestCase { doMotionEvent(MotionEvent.ACTION_DOWN, 0); verifyNoMoreInteractions(mIOnBackInvokedCallback); mController.onBackAnimationFinished(); + // Pretend the transition handler called finishAnimation. + mController.finishBackNavigation(); // Verify that more events from a rejected swipe cannot start animation. doMotionEvent(MotionEvent.ACTION_MOVE, 100); @@ -278,6 +284,8 @@ public class BackAnimationControllerTest extends ShellTestCase { // Simulate transition timeout. mShellExecutor.flushAll(); mController.onBackAnimationFinished(); + // Pretend the transition handler called finishAnimation. + mController.finishBackNavigation(); doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d42ad58f6f217..e9774479233dd 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -43,6 +43,7 @@ import android.window.IBackAnimationFinishedCallback; import android.window.OnBackInvokedCallbackInfo; import android.window.ScreenCapture; import android.window.TaskSnapshot; +import android.window.WindowContainerToken; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -61,6 +62,9 @@ class BackNavigationController { private boolean mShowWallpaper; private Runnable mPendingAnimation; + // TODO (b/241808055) Find a appropriate time to remove during refactor + // Execute back animation with legacy transition system. Temporary flag for easier debugging. + static final boolean ENABLE_SHELL_TRANSITIONS = WindowManagerService.sEnableShellTransitions; /** * Returns true if the back predictability feature is enabled */ @@ -263,12 +267,14 @@ class BackNavigationController { // Only prepare animation if no leash has been created (no animation is running). // TODO(b/241808055): Cancel animation when preparing back animation. if (prepareAnimation - && removedWindowContainer.hasCommittedReparentToAnimationLeash()) { + && (removedWindowContainer.hasCommittedReparentToAnimationLeash() + || removedWindowContainer.mTransitionController.inTransition())) { Slog.w(TAG, "Can't prepare back animation due to another animation is running."); prepareAnimation = false; } if (prepareAnimation) { + infoBuilder.setDepartingWCT(toWindowContainerToken(currentTask)); prepareAnimationIfNeeded(currentTask, prevTask, prevActivity, removedWindowContainer, backType, adapter); } @@ -287,6 +293,13 @@ class BackNavigationController { return infoBuilder.build(); } + private static WindowContainerToken toWindowContainerToken(WindowContainer windowContainer) { + if (windowContainer == null || windowContainer.mRemoteToken == null) { + return null; + } + return windowContainer.mRemoteToken.toWindowContainerToken(); + } + private void prepareAnimationIfNeeded(Task currentTask, Task prevTask, ActivityRecord prevActivity, WindowContainer removedWindowContainer, int backType, BackAnimationAdapter adapter) { @@ -352,8 +365,10 @@ class BackNavigationController { leashes.add(screenshotSurface); } } else if (prevTask != null) { - // Special handling for preventing next transition. - currentTask.mBackGestureStarted = true; + if (!ENABLE_SHELL_TRANSITIONS) { + // Special handling for preventing next transition. + currentTask.mBackGestureStarted = true; + } prevActivity = prevTask.getTopNonFinishingActivity(); if (prevActivity != null) { // Make previous task show from behind by marking its top activity as visible @@ -396,16 +411,27 @@ class BackNavigationController { } synchronized (mWindowManagerService.mGlobalLock) { - if (triggerBack) { - final SurfaceControl surfaceControl = - removedWindowContainer.getSurfaceControl(); - if (surfaceControl != null && surfaceControl.isValid()) { - // When going back to home, hide the task surface before it is - // re-parented to avoid flicker. - finishedTransaction.hide(surfaceControl); + if (ENABLE_SHELL_TRANSITIONS) { + if (!triggerBack) { + if (!needsScreenshot(backType)) { + restoreLaunchBehind(finalPrevActivity); + } + } + } else { + if (triggerBack) { + final SurfaceControl surfaceControl = + removedWindowContainer.getSurfaceControl(); + if (surfaceControl != null && surfaceControl.isValid()) { + // When going back to home, hide the task surface before it + // re-parented to avoid flicker. + finishedTransaction.hide(surfaceControl); + } + } else { + currentTask.mBackGestureStarted = false; + if (!needsScreenshot(backType)) { + restoreLaunchBehind(finalPrevActivity); + } } - } else if (!needsScreenshot(backType)) { - restoreLaunchBehind(finalPrevActivity); } } finishedTransaction.apply();