From 8fcc92bb3421e2e27b052ad3d8b957a5ec311ba8 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Tue, 16 Aug 2022 08:17:08 +0000 Subject: [PATCH 1/3] Refactor back navigation animtion (1/2) In previous design, it would create all necessary leashes when starting back navigation, and they would be carried by `BackNavigationInfo` and `BackEvent` and would finally deliver to the shell and animator side. In this CL, we will use the adapter that wraps a back animation runner to deliver all leashes in next surface placement after back navigation has started. In shell side, every animator should be registered by type, so the adapter could deliver leashes via IRemoteAnimationRunner to the target animator, and invoke callback when it finished. This also eliminated all unecessary fields from `BackNavigationInfo` and `BackEvent`. Bug: 241808055 Test: atest BackNavigationControllerTests BackAnimationControllerTest BackNavigationTest Change-Id: I8bbec0d8d9631110c3d2788d958b50ae487520a7 --- .../android/app/IActivityTaskManager.aidl | 8 +- .../android/window/BackAnimationAdapter.aidl | 22 + .../android/window/BackAnimationAdapter.java | 62 +++ core/java/android/window/BackEvent.java | 23 +- .../android/window/BackNavigationInfo.java | 161 ++------ .../IBackAnimationFinishedCallback.aidl | 27 ++ .../android/window/IBackAnimationRunner.aidl | 52 +++ .../android/window/BackNavigationTest.java | 2 +- data/etc/services.core.protolog.json | 12 +- .../shell/back/BackAnimationController.java | 294 ++++++++------ .../wm/shell/back/BackAnimationRunner.java | 108 +++++ .../android/wm/shell/back/IBackAnimation.aidl | 16 +- .../back/BackAnimationControllerTest.java | 133 +++--- .../server/wm/ActivityTaskManagerService.java | 8 +- .../server/wm/BackNavigationController.java | 384 +++++++++++------- .../wm/BackNavigationControllerTests.java | 25 +- 16 files changed, 792 insertions(+), 545 deletions(-) create mode 100644 core/java/android/window/BackAnimationAdapter.aidl create mode 100644 core/java/android/window/BackAnimationAdapter.java create mode 100644 core/java/android/window/IBackAnimationFinishedCallback.aidl create mode 100644 core/java/android/window/IBackAnimationRunner.aidl create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 6576a1a5d3c2a..9bad0a0544b13 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -73,6 +73,7 @@ import android.view.IWindowFocusObserver; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationAdapter; import android.window.IWindowOrganizerController; +import android.window.BackAnimationAdapter; import android.window.BackNavigationInfo; import android.window.SplashScreenView; import com.android.internal.app.IVoiceInteractor; @@ -352,9 +353,10 @@ interface IActivityTaskManager { /** * Prepare the back navigation in the server. This setups the leashed for sysui to animate * the back gesture and returns the data needed for the animation. - * @param requestAnimation true if the caller wishes to animate the back navigation * @param focusObserver a remote callback to nofify shell when the focused window lost focus. + * @param adaptor a remote animation to be run for the back navigation plays the animation. + * @return Returns the back navigation info. */ - android.window.BackNavigationInfo startBackNavigation(in boolean requestAnimation, - in IWindowFocusObserver focusObserver); + android.window.BackNavigationInfo startBackNavigation( + in IWindowFocusObserver focusObserver, in BackAnimationAdapter adaptor); } diff --git a/core/java/android/window/BackAnimationAdapter.aidl b/core/java/android/window/BackAnimationAdapter.aidl new file mode 100644 index 0000000000000..2d7126c02a0d8 --- /dev/null +++ b/core/java/android/window/BackAnimationAdapter.aidl @@ -0,0 +1,22 @@ +/* + * 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 android.window; + +/** + * @hide + */ +parcelable BackAnimationAdapter; \ No newline at end of file diff --git a/core/java/android/window/BackAnimationAdapter.java b/core/java/android/window/BackAnimationAdapter.java new file mode 100644 index 0000000000000..5eb34e694a572 --- /dev/null +++ b/core/java/android/window/BackAnimationAdapter.java @@ -0,0 +1,62 @@ +/* + * 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 android.window; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Object that describes how to run a remote back animation. + * + * @hide + */ +public class BackAnimationAdapter implements Parcelable { + private final IBackAnimationRunner mRunner; + + public BackAnimationAdapter(IBackAnimationRunner runner) { + mRunner = runner; + } + + public BackAnimationAdapter(Parcel in) { + mRunner = IBackAnimationRunner.Stub.asInterface(in.readStrongBinder()); + } + + public IBackAnimationRunner getRunner() { + return mRunner; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongInterface(mRunner); + } + + public static final @android.annotation.NonNull Creator CREATOR = + new Creator() { + public BackAnimationAdapter createFromParcel(Parcel in) { + return new BackAnimationAdapter(in); + } + + public BackAnimationAdapter[] newArray(int size) { + return new BackAnimationAdapter[size]; + } + }; +} diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java index 1024e2e50c3e2..4a4f561c71ede 100644 --- a/core/java/android/window/BackEvent.java +++ b/core/java/android/window/BackEvent.java @@ -18,10 +18,8 @@ package android.window; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import android.view.RemoteAnimationTarget; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -52,8 +50,6 @@ public class BackEvent implements Parcelable { @SwipeEdge private final int mSwipeEdge; - @Nullable - private final RemoteAnimationTarget mDepartingAnimationTarget; /** * Creates a new {@link BackEvent} instance. @@ -62,16 +58,12 @@ public class BackEvent implements Parcelable { * @param touchY Absolute Y location of the touch point of this event. * @param progress Value between 0 and 1 on how far along the back gesture is. * @param swipeEdge Indicates which edge the swipe starts from. - * @param departingAnimationTarget The remote animation target of the departing application - * window. */ - public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge, - @Nullable RemoteAnimationTarget departingAnimationTarget) { + public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge) { mTouchX = touchX; mTouchY = touchY; mProgress = progress; mSwipeEdge = swipeEdge; - mDepartingAnimationTarget = departingAnimationTarget; } private BackEvent(@NonNull Parcel in) { @@ -79,7 +71,6 @@ public class BackEvent implements Parcelable { mTouchY = in.readFloat(); mProgress = in.readFloat(); mSwipeEdge = in.readInt(); - mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); } public static final Creator CREATOR = new Creator() { @@ -105,7 +96,6 @@ public class BackEvent implements Parcelable { dest.writeFloat(mTouchY); dest.writeFloat(mProgress); dest.writeInt(mSwipeEdge); - dest.writeTypedObject(mDepartingAnimationTarget, flags); } /** @@ -136,16 +126,6 @@ public class BackEvent implements Parcelable { return mSwipeEdge; } - /** - * Returns the {@link RemoteAnimationTarget} of the top departing application window, - * or {@code null} if the top window should not be moved for the current type of back - * destination. - */ - @Nullable - public RemoteAnimationTarget getDepartingAnimationTarget() { - return mDepartingAnimationTarget; - } - @Override public String toString() { return "BackEvent{" @@ -153,7 +133,6 @@ public class BackEvent implements Parcelable { + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress + ", mSwipeEdge" + mSwipeEdge - + ", mDepartingAnimationTarget" + mDepartingAnimationTarget + "}"; } } diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index dd49014176711..87cfbb2a57055 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -19,14 +19,10 @@ package android.window; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.WindowConfiguration; -import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteCallback; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; /** * Information to be sent to SysUI about a back event. @@ -84,75 +80,50 @@ public final class BackNavigationInfo implements Parcelable { TYPE_CROSS_TASK, TYPE_CALLBACK }) - @interface BackTargetType { + public @interface BackTargetType { } private final int mType; @Nullable - private final RemoteAnimationTarget mDepartingAnimationTarget; - @Nullable - private final SurfaceControl mScreenshotSurface; - @Nullable - private final HardwareBuffer mScreenshotBuffer; - @Nullable private final RemoteCallback mOnBackNavigationDone; @Nullable - private final WindowConfiguration mTaskWindowConfiguration; - @Nullable private final IOnBackInvokedCallback mOnBackInvokedCallback; + private final boolean mPrepareRemoteAnimation; /** * Create a new {@link BackNavigationInfo} instance. * * @param type The {@link BackTargetType} of the destination (what will be - * displayed after the back action). - * @param departingAnimationTarget The remote animation target, containing a leash to animate - * away the departing window. The consumer of the leash is - * responsible for removing it. - * @param screenshotSurface The screenshot of the previous activity to be displayed. - * @param screenshotBuffer A buffer containing a screenshot used to display the activity. - * See {@link #getScreenshotHardwareBuffer()} for information - * about nullity. - * @param taskWindowConfiguration The window configuration of the Task being animated beneath. * @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 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. */ private BackNavigationInfo(@BackTargetType int type, - @Nullable RemoteAnimationTarget departingAnimationTarget, - @Nullable SurfaceControl screenshotSurface, - @Nullable HardwareBuffer screenshotBuffer, - @Nullable WindowConfiguration taskWindowConfiguration, @Nullable RemoteCallback onBackNavigationDone, - @Nullable IOnBackInvokedCallback onBackInvokedCallback) { + @Nullable IOnBackInvokedCallback onBackInvokedCallback, + boolean isPrepareRemoteAnimation) { mType = type; - mDepartingAnimationTarget = departingAnimationTarget; - mScreenshotSurface = screenshotSurface; - mScreenshotBuffer = screenshotBuffer; - mTaskWindowConfiguration = taskWindowConfiguration; mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; + mPrepareRemoteAnimation = isPrepareRemoteAnimation; } private BackNavigationInfo(@NonNull Parcel in) { mType = in.readInt(); - mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); - mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR); - mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR); - mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR); mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR); mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); + mPrepareRemoteAnimation = in.readBoolean(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); - dest.writeTypedObject(mDepartingAnimationTarget, flags); - dest.writeTypedObject(mScreenshotSurface, flags); - dest.writeTypedObject(mScreenshotBuffer, flags); - dest.writeTypedObject(mTaskWindowConfiguration, flags); dest.writeTypedObject(mOnBackNavigationDone, flags); dest.writeStrongInterface(mOnBackInvokedCallback); + dest.writeBoolean(mPrepareRemoteAnimation); } /** @@ -164,49 +135,6 @@ public final class BackNavigationInfo implements Parcelable { return mType; } - /** - * Returns a {@link RemoteAnimationTarget}, containing a leash to the top window container - * that needs to be animated. This can be null if the back animation is controlled by - * the application. - */ - @Nullable - public RemoteAnimationTarget getDepartingAnimationTarget() { - return mDepartingAnimationTarget; - } - - /** - * Returns the {@link SurfaceControl} that should be used to display a screenshot of the - * previous activity. - */ - @Nullable - public SurfaceControl getScreenshotSurface() { - return mScreenshotSurface; - } - - /** - * Returns the {@link HardwareBuffer} containing the screenshot the activity about to be - * shown. This can be null if one of the following conditions is met: - *
    - *
  • The screenshot is not available - *
  • The previous activity is the home screen ( {@link #TYPE_RETURN_TO_HOME} - *
  • The current window is a dialog ({@link #TYPE_DIALOG_CLOSE} - *
  • The back animation is controlled by the application - *
- */ - @Nullable - public HardwareBuffer getScreenshotHardwareBuffer() { - return mScreenshotBuffer; - } - - /** - * Returns the {@link WindowConfiguration} of the current task. This is null when the top - * application is controlling the back animation. - */ - @Nullable - public WindowConfiguration getTaskWindowConfiguration() { - return mTaskWindowConfiguration; - } - /** * Returns the {@link OnBackInvokedCallback} of the top level window or null if * the client didn't register a callback. @@ -221,6 +149,13 @@ public final class BackNavigationInfo implements Parcelable { return mOnBackInvokedCallback; } + /** + * Return true if the core is preparing a back gesture nimation. + */ + public boolean isPrepareRemoteAnimation() { + return mPrepareRemoteAnimation; + } + /** * 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. @@ -256,10 +191,6 @@ public final class BackNavigationInfo implements Parcelable { public String toString() { return "BackNavigationInfo{" + "mType=" + typeToString(mType) + " (" + mType + ")" - + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget - + ", mScreenshotSurface=" + mScreenshotSurface - + ", mTaskWindowConfiguration= " + mTaskWindowConfiguration - + ", mScreenshotBuffer=" + mScreenshotBuffer + ", mOnBackNavigationDone=" + mOnBackNavigationDone + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback + '}'; @@ -291,21 +222,12 @@ public final class BackNavigationInfo implements Parcelable { */ @SuppressWarnings("UnusedReturnValue") // Builder pattern public static class Builder { - private int mType = TYPE_UNDEFINED; @Nullable - private RemoteAnimationTarget mDepartingAnimationTarget = null; - @Nullable - private SurfaceControl mScreenshotSurface = null; - @Nullable - private HardwareBuffer mScreenshotBuffer = null; - @Nullable - private WindowConfiguration mTaskWindowConfiguration = null; - @Nullable private RemoteCallback mOnBackNavigationDone = null; @Nullable private IOnBackInvokedCallback mOnBackInvokedCallback = null; - + private boolean mPrepareRemoteAnimation; /** * @see BackNavigationInfo#getType() */ @@ -314,40 +236,6 @@ public final class BackNavigationInfo implements Parcelable { return this; } - /** - * @see BackNavigationInfo#getDepartingAnimationTarget - */ - public Builder setDepartingAnimationTarget( - @Nullable RemoteAnimationTarget departingAnimationTarget) { - mDepartingAnimationTarget = departingAnimationTarget; - return this; - } - - /** - * @see BackNavigationInfo#getScreenshotSurface - */ - public Builder setScreenshotSurface(@Nullable SurfaceControl screenshotSurface) { - mScreenshotSurface = screenshotSurface; - return this; - } - - /** - * @see BackNavigationInfo#getScreenshotHardwareBuffer() - */ - public Builder setScreenshotBuffer(@Nullable HardwareBuffer screenshotBuffer) { - mScreenshotBuffer = screenshotBuffer; - return this; - } - - /** - * @see BackNavigationInfo#getTaskWindowConfiguration - */ - public Builder setTaskWindowConfiguration( - @Nullable WindowConfiguration taskWindowConfiguration) { - mTaskWindowConfiguration = taskWindowConfiguration; - return this; - } - /** * @see BackNavigationInfo#onBackNavigationFinished(boolean) */ @@ -365,13 +253,20 @@ public final class BackNavigationInfo implements Parcelable { return this; } + /** + * @param prepareRemoteAnimation Whether core prepare animation for shell. + */ + public Builder setPrepareRemoteAnimation(boolean prepareRemoteAnimation) { + mPrepareRemoteAnimation = prepareRemoteAnimation; + return this; + } + /** * Builds and returns an instance of {@link BackNavigationInfo} */ public BackNavigationInfo build() { - return new BackNavigationInfo(mType, mDepartingAnimationTarget, mScreenshotSurface, - mScreenshotBuffer, mTaskWindowConfiguration, mOnBackNavigationDone, - mOnBackInvokedCallback); + return new BackNavigationInfo(mType, mOnBackNavigationDone, + mOnBackInvokedCallback, mPrepareRemoteAnimation); } } } diff --git a/core/java/android/window/IBackAnimationFinishedCallback.aidl b/core/java/android/window/IBackAnimationFinishedCallback.aidl new file mode 100644 index 0000000000000..8afc003256edd --- /dev/null +++ b/core/java/android/window/IBackAnimationFinishedCallback.aidl @@ -0,0 +1,27 @@ +/* + * 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 android.window; + +/** + * Interface to be invoked by the controlling process when a back animation has finished. + * + * @param trigger Whether the back gesture has passed the triggering threshold. + * {@hide} + */ +oneway interface IBackAnimationFinishedCallback { + void onAnimationFinished(in boolean triggerBack); +} \ No newline at end of file diff --git a/core/java/android/window/IBackAnimationRunner.aidl b/core/java/android/window/IBackAnimationRunner.aidl new file mode 100644 index 0000000000000..1c677896dbd91 --- /dev/null +++ b/core/java/android/window/IBackAnimationRunner.aidl @@ -0,0 +1,52 @@ +/* + * 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 android.window; + +import android.view.RemoteAnimationTarget; +import android.window.IBackAnimationFinishedCallback; + +/** + * Interface that is used to callback from window manager to the process that runs a back gesture + * animation to start or cancel it. + * + * {@hide} + */ +oneway interface IBackAnimationRunner { + + /** + * Called when the system needs to cancel the current animation. This can be due to the + * wallpaper not drawing in time, or the handler not finishing the animation within a predefined + * amount of time. + * + */ + void onAnimationCancelled() = 1; + + /** + * Called when the system is ready for the handler to start animating all the visible tasks. + * @param type The back navigation type. + * @param apps The list of departing (type=MODE_CLOSING) and entering (type=MODE_OPENING) + windows to animate, + * @param wallpapers The list of wallpapers to animate. + * @param nonApps The list of non-app windows such as Bubbles to animate. + * @param finishedCallback The callback to invoke when the animation is finished. + */ + void onAnimationStart(in int type, + in RemoteAnimationTarget[] apps, + in RemoteAnimationTarget[] wallpapers, + in RemoteAnimationTarget[] nonApps, + in IBackAnimationFinishedCallback finishedCallback) = 2; +} \ No newline at end of file diff --git a/core/tests/coretests/src/android/window/BackNavigationTest.java b/core/tests/coretests/src/android/window/BackNavigationTest.java index bbbc4230903ab..d6145ebbbdb00 100644 --- a/core/tests/coretests/src/android/window/BackNavigationTest.java +++ b/core/tests/coretests/src/android/window/BackNavigationTest.java @@ -92,7 +92,7 @@ public class BackNavigationTest { try { mInstrumentation.getUiAutomation().waitForIdle(500, 1000); BackNavigationInfo info = ActivityTaskManager.getService() - .startBackNavigation(true, null); + .startBackNavigation(null, null); assertNotNull("BackNavigationInfo is null", info); assertNotNull("OnBackInvokedCallback is null", info.getOnBackInvokedCallback()); info.getOnBackInvokedCallback().onBackInvoked(); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index cbdef846ceaf5..f9f2906316f40 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1111,6 +1111,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1033630971": { + "message": "onBackNavigationDone backType=%s, triggerBack=%b", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-1022146708": { "message": "Skipping %s: mismatch activity type", "level": "DEBUG", @@ -3991,12 +3997,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1778919449": { - "message": "onBackNavigationDone backType=%s, task=%s, prevActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "1781673113": { "message": "onAnimationFinished(): targetRootTask=%s targetActivity=%s mRestoreTargetBehindRootTask=%s", "level": "DEBUG", 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 ebf8c0354c1bb..cd77802fa10bb 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 @@ -23,13 +23,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; -import android.app.WindowConfiguration; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; -import android.graphics.Point; -import android.graphics.PointF; -import android.hardware.HardwareBuffer; import android.hardware.input.InputManager; import android.net.Uri; import android.os.Handler; @@ -40,15 +36,19 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.Log; +import android.util.SparseArray; +import android.view.IRemoteAnimationRunner; import android.view.IWindowFocusObserver; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; +import android.window.BackAnimationAdapter; import android.window.BackEvent; import android.window.BackNavigationInfo; +import android.window.IBackAnimationFinishedCallback; +import android.window.IBackAnimationRunner; import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; @@ -83,16 +83,6 @@ public class BackAnimationController implements RemoteCallable { - finishAnimation(); - mTransitionInProgress = false; ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Transition didn't finish in %d ms. Resetting...", MAX_TRANSITION_DURATION); + onBackAnimationFinished(); }; + private IBackAnimationFinishedCallback mBackAnimationFinishedCallback; + @VisibleForTesting + BackAnimationAdapter mBackAnimationAdapter; + + private final TouchTracker mTouchTracker = new TouchTracker(); + + private final SparseArray mAnimationDefinition = new SparseArray<>(); + @VisibleForTesting final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() { @Override @@ -134,18 +126,66 @@ public class BackAnimationController implements RemoteCallable= 0 + ? PROGRESS_THRESHOLD : mProgressThreshold; + return Math.min(Math.max(Math.abs(deltaX) / progressThreshold, 0), 1); + } + + void reset() { + mInitTouchX = 0; + mInitTouchY = 0; + mSwipeEdge = -1; + } + } + public BackAnimationController( @NonNull ShellInit shellInit, @NonNull @ShellMainThread ShellExecutor shellExecutor, @NonNull @ShellBackgroundThread Handler backgroundHandler, Context context) { - this(shellInit, shellExecutor, backgroundHandler, new SurfaceControl.Transaction(), + this(shellInit, shellExecutor, backgroundHandler, ActivityTaskManager.getService(), context, context.getContentResolver()); } @@ -154,11 +194,9 @@ public class BackAnimationController implements RemoteCallable controller.setBackToLauncherCallback(callback)); + (controller) -> controller.setBackToLauncherCallback(callback, runner)); } @Override @@ -261,28 +301,30 @@ public class BackAnimationController implements RemoteCallable controller.clearBackToLauncherCallback()); } - @Override - public void onBackToLauncherAnimationFinished() { - executeRemoteCallWithTaskPermission(mController, "onBackToLauncherAnimationFinished", - (controller) -> controller.onBackToLauncherAnimationFinished()); - } - void invalidate() { mController = null; } } @VisibleForTesting - void setBackToLauncherCallback(IOnBackInvokedCallback callback) { - mBackToLauncherCallback = callback; + void setBackToLauncherCallback(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner) { + mAnimationDefinition.set(BackNavigationInfo.TYPE_RETURN_TO_HOME, + new BackAnimationRunner(callback, runner)); } private void clearBackToLauncherCallback() { - mBackToLauncherCallback = null; + mAnimationDefinition.remove(BackNavigationInfo.TYPE_RETURN_TO_HOME); } @VisibleForTesting - void onBackToLauncherAnimationFinished() { + void onBackAnimationFinished() { + if (!mTransitionInProgress) { + return; + } + + ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()"); + + // Trigger real back. if (mBackNavigationInfo != null) { IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); if (mTriggerBack) { @@ -303,6 +345,7 @@ public class BackAnimationController implements RemoteCallable= 0 ? PROGRESS_THRESHOLD : mProgressThreshold; - float progress = Math.min(Math.max(Math.abs(deltaX) / progressThreshold, 0), 1); + mTouchTracker.update(touchX, touchY, swipeEdge); + float progress = mTouchTracker.getProgress(touchX); int backType = mBackNavigationInfo.getType(); - RemoteAnimationTarget animationTarget = mBackNavigationInfo.getDepartingAnimationTarget(); - BackEvent backEvent = new BackEvent( - touchX, touchY, progress, swipeEdge, animationTarget); + BackEvent backEvent = new BackEvent(touchX, touchY, progress, swipeEdge); IOnBackInvokedCallback targetCallback = null; - if (shouldDispatchToLauncher(backType)) { - targetCallback = mBackToLauncherCallback; - } else if (backType == BackNavigationInfo.TYPE_CROSS_TASK - || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) { - // TODO(208427216) Run the actual animation - } else if (backType == BackNavigationInfo.TYPE_CALLBACK) { + if (shouldDispatchToAnimator(backType)) { + targetCallback = mAnimationDefinition.get(backType).getCallback(); + } else { targetCallback = mBackNavigationInfo.getOnBackInvokedCallback(); } dispatchOnBackProgressed(targetCallback, backEvent); @@ -473,11 +473,16 @@ public class BackAnimationController implements RemoteCallable { + final BackAnimationRunner runner = mAnimationDefinition.get(type); + if (runner == null) { + Log.e(TAG, "Animation didn't be defined for type " + + BackNavigationInfo.typeToString(type)); + if (finishedCallback != null) { + try { + finishedCallback.onAnimationFinished(false); + } catch (RemoteException e) { + Log.w(TAG, "Failed call IBackNaviAnimationController", e); + } + } + return; + } + mBackAnimationFinishedCallback = finishedCallback; + + ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()"); + runner.startAnimation(apps, wallpapers, nonApps, + BackAnimationController.this::onBackAnimationFinished); + + if (!mBackGestureStarted) { + // if the down -> up gesture happened before animation start, we have to + // trigger the uninterruptible transition to finish the back animation. + final BackEvent backFinish = new BackEvent( + mTouchTracker.mLatestTouchX, mTouchTracker.mLatestTouchY, 1, + mTouchTracker.mSwipeEdge); + startTransition(); + runner.consumeIfGestureFinished(backFinish); + } + }); + } + + @Override + public void onAnimationCancelled() { } + }; + mBackAnimationAdapter = new BackAnimationAdapter(runner); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java new file mode 100644 index 0000000000000..12bbf73af5616 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java @@ -0,0 +1,108 @@ +/* + * 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 static android.view.WindowManager.TRANSIT_OLD_UNSET; + +import android.os.RemoteException; +import android.util.Log; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.window.BackEvent; +import android.window.IBackAnimationRunner; +import android.window.IOnBackInvokedCallback; + +/** + * Used to register the animation callback and runner, it will trigger result if gesture was finish + * before it received IBackAnimationRunner#onAnimationStart, so the controller could continue + * trigger the real back behavior. + */ +class BackAnimationRunner { + private static final String TAG = "ShellBackPreview"; + + private final IOnBackInvokedCallback mCallback; + private final IRemoteAnimationRunner mRunner; + + private boolean mTriggerBack; + // Whether we are waiting to receive onAnimationStart + private boolean mWaitingAnimation; + + BackAnimationRunner(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner) { + mCallback = callback; + mRunner = runner; + } + + /** Returns the registered animation runner */ + IRemoteAnimationRunner getRunner() { + return mRunner; + } + + /** Returns the registered animation callback */ + IOnBackInvokedCallback getCallback() { + return mCallback; + } + + /** + * Called from {@link IBackAnimationRunner}, it will deliver these + * {@link RemoteAnimationTarget}s to the corresponding runner. + */ + void startAnimation(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, Runnable finishedCallback) { + final IRemoteAnimationFinishedCallback callback = + new IRemoteAnimationFinishedCallback.Stub() { + @Override + public void onAnimationFinished() { + finishedCallback.run(); + } + }; + mWaitingAnimation = false; + try { + mRunner.onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers, + nonApps, callback); + } catch (RemoteException e) { + Log.w(TAG, "Failed call onAnimationStart", e); + } + } + + IOnBackInvokedCallback getGestureStartedCallback() { + mWaitingAnimation = true; + return mCallback; + } + + boolean onGestureFinished(boolean triggerBack) { + if (mWaitingAnimation) { + mTriggerBack = triggerBack; + return true; + } + return false; + } + + void consumeIfGestureFinished(final BackEvent backFinish) { + Log.d(TAG, "Start transition due to gesture is finished"); + try { + mCallback.onBackProgressed(backFinish); + if (mTriggerBack) { + mCallback.onBackInvoked(); + } else { + mCallback.onBackCancelled(); + } + } catch (RemoteException e) { + Log.e(TAG, "dispatch error: ", e); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl index 6311f879fd459..2b2a0e3977925 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl @@ -17,29 +17,21 @@ package com.android.wm.shell.back; import android.window.IOnBackInvokedCallback; +import android.view.IRemoteAnimationRunner; /** * Interface for Launcher process to register back invocation callbacks. */ interface IBackAnimation { - /** - * Sets a {@link IOnBackInvokedCallback} to be invoked when + * Sets a {@link IOnBackInvokedCallback} and a {@link IRemoteAnimationRunner} to be invoked when * back navigation has type {@link BackNavigationInfo#TYPE_RETURN_TO_HOME}. */ - void setBackToLauncherCallback(in IOnBackInvokedCallback callback); + void setBackToLauncherCallback(in IOnBackInvokedCallback callback, + in IRemoteAnimationRunner runner); /** * Clears the previously registered {@link IOnBackInvokedCallback}. */ void clearBackToLauncherCallback(); - - /** - * Notifies Shell that the back to launcher animation has fully finished - * (including the transition animation that runs after the finger is lifted). - * - * At this point the top window leash (if one was created) should be ready to be released. - * //TODO: Remove once we play the transition animation through shell transitions. - */ - void onBackToLauncherAnimationFinished(); } 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 5b3b8fd7ad712..9f39598e70a52 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 @@ -18,12 +18,9 @@ package com.android.wm.shell.back; import static android.window.BackNavigationInfo.KEY_TRIGGER_BACK; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -39,7 +36,6 @@ import android.app.WindowConfiguration; import android.content.pm.ApplicationInfo; import android.graphics.Point; import android.graphics.Rect; -import android.hardware.HardwareBuffer; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallback; @@ -49,11 +45,13 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableContentResolver; import android.testing.TestableContext; import android.testing.TestableLooper; +import android.view.IRemoteAnimationRunner; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.BackEvent; import android.window.BackNavigationInfo; +import android.window.IBackAnimationFinishedCallback; import android.window.IOnBackInvokedCallback; import androidx.test.filters.SmallTest; @@ -65,7 +63,6 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.sysui.ShellInit; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -89,15 +86,18 @@ public class BackAnimationControllerTest extends ShellTestCase { public TestableContext mContext = new TestableContext(InstrumentationRegistry.getInstrumentation().getContext()); - @Mock - private SurfaceControl.Transaction mTransaction; - @Mock private IActivityTaskManager mActivityTaskManager; @Mock private IOnBackInvokedCallback mIOnBackInvokedCallback; + @Mock + private IBackAnimationFinishedCallback mBackAnimationFinishedCallback; + + @Mock + private IRemoteAnimationRunner mBackAnimationRunner; + private BackAnimationController mController; private int mEventTime = 0; @@ -115,7 +115,7 @@ public class BackAnimationControllerTest extends ShellTestCase { mTestableLooper = TestableLooper.get(this); mShellInit = spy(new ShellInit(mShellExecutor)); mController = new BackAnimationController(mShellInit, - mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction, + mShellExecutor, new Handler(mTestableLooper.getLooper()), mActivityTaskManager, mContext, mContentResolver); mShellInit.init(); @@ -123,19 +123,12 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellExecutor.flushAll(); } - private void createNavigationInfo(RemoteAnimationTarget topAnimationTarget, - SurfaceControl screenshotSurface, - HardwareBuffer hardwareBuffer, - int backType, - IOnBackInvokedCallback onBackInvokedCallback) { + private void createNavigationInfo(int backType, IOnBackInvokedCallback onBackInvokedCallback) { BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() .setType(backType) - .setDepartingAnimationTarget(topAnimationTarget) - .setScreenshotSurface(screenshotSurface) - .setScreenshotBuffer(hardwareBuffer) - .setTaskWindowConfiguration(new WindowConfiguration()) .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) - .setOnBackInvokedCallback(onBackInvokedCallback); + .setOnBackInvokedCallback(onBackInvokedCallback) + .setPrepareRemoteAnimation(true); createNavigationInfo(builder); } @@ -143,7 +136,7 @@ public class BackAnimationControllerTest extends ShellTestCase { private void createNavigationInfo(BackNavigationInfo.Builder builder) { try { doReturn(builder.build()).when(mActivityTaskManager) - .startBackNavigation(anyBoolean(), any()); + .startBackNavigation(any(), any()); } catch (RemoteException ex) { ex.rethrowFromSystemServer(); } @@ -169,43 +162,12 @@ public class BackAnimationControllerTest extends ShellTestCase { verify(mShellInit, times(1)).addInitCallback(any(), any()); } - @Test - @Ignore("b/207481538") - public void crossActivity_screenshotAttachedAndVisible() { - SurfaceControl screenshotSurface = new SurfaceControl(); - HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class); - createNavigationInfo(createAnimationTarget(), screenshotSurface, hardwareBuffer, - BackNavigationInfo.TYPE_CROSS_ACTIVITY, null); - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer); - verify(mTransaction).setVisibility(screenshotSurface, true); - verify(mTransaction).apply(); - } - - @Test - public void crossActivity_surfaceMovesWithGesture() { - SurfaceControl screenshotSurface = new SurfaceControl(); - HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class); - RemoteAnimationTarget animationTarget = createAnimationTarget(); - createNavigationInfo(animationTarget, screenshotSurface, hardwareBuffer, - BackNavigationInfo.TYPE_CROSS_ACTIVITY, null); - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - doMotionEvent(MotionEvent.ACTION_MOVE, 100); - // b/207481538, we check that the surface is not moved for now, we can re-enable this once - // we implement the animation - verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt()); - verify(mTransaction, never()).setPosition( - animationTarget.leash, 100, 100); - verify(mTransaction, atLeastOnce()).apply(); - } - @Test public void verifyAnimationFinishes() { RemoteAnimationTarget animationTarget = createAnimationTarget(); boolean[] backNavigationDone = new boolean[]{false}; boolean[] triggerBack = new boolean[]{false}; createNavigationInfo(new BackNavigationInfo.Builder() - .setDepartingAnimationTarget(animationTarget) .setType(BackNavigationInfo.TYPE_CROSS_ACTIVITY) .setOnBackNavigationDone( new RemoteCallback(result -> { @@ -219,19 +181,19 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void backToHome_dispatchesEvents() throws RemoteException { - mController.setBackToLauncherCallback(mIOnBackInvokedCallback); - RemoteAnimationTarget animationTarget = createAnimationTarget(); - createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, mIOnBackInvokedCallback); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. doMotionEvent(MotionEvent.ACTION_MOVE, 100); + + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); verify(mIOnBackInvokedCallback).onBackStarted(); + verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); ArgumentCaptor backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); - verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture()); - assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget()); + verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture()); // Check that back invocation is dispatched. mController.setTriggerBack(true); // Fake trigger back @@ -245,17 +207,16 @@ public class BackAnimationControllerTest extends ShellTestCase { Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0"); ShellInit shellInit = new ShellInit(mShellExecutor); mController = new BackAnimationController(shellInit, - mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction, + mShellExecutor, new Handler(mTestableLooper.getLooper()), mActivityTaskManager, mContext, mContentResolver); shellInit.init(); - mController.setBackToLauncherCallback(mIOnBackInvokedCallback); + mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner); - RemoteAnimationTarget animationTarget = createAnimationTarget(); IOnBackInvokedCallback appCallback = mock(IOnBackInvokedCallback.class); ArgumentCaptor backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); - createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback); + + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback); triggerBackGesture(); @@ -266,25 +227,29 @@ public class BackAnimationControllerTest extends ShellTestCase { verify(mIOnBackInvokedCallback, never()).onBackStarted(); verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture()); verify(mIOnBackInvokedCallback, never()).onBackInvoked(); + verify(mBackAnimationRunner, never()).onAnimationStart( + anyInt(), any(), any(), any(), any()); } @Test public void ignoresGesture_transitionInProgress() throws RemoteException { - mController.setBackToLauncherCallback(mIOnBackInvokedCallback); - RemoteAnimationTarget animationTarget = createAnimationTarget(); - createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null); triggerBackGesture(); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); // Check that back invocation is dispatched. verify(mIOnBackInvokedCallback).onBackInvoked(); + verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); reset(mIOnBackInvokedCallback); + reset(mBackAnimationRunner); + // Verify that we prevent animation from restarting if another gestures happens before // the previous transition is finished. doMotionEvent(MotionEvent.ACTION_DOWN, 0); verifyNoMoreInteractions(mIOnBackInvokedCallback); - mController.onBackToLauncherAnimationFinished(); + mController.onBackAnimationFinished(); // Verify that more events from a rejected swipe cannot start animation. doMotionEvent(MotionEvent.ACTION_MOVE, 100); @@ -294,39 +259,47 @@ public class BackAnimationControllerTest extends ShellTestCase { // Verify that we start accepting gestures again once transition finishes. doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); + + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); verify(mIOnBackInvokedCallback).onBackStarted(); + verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); } @Test public void acceptsGesture_transitionTimeout() throws RemoteException { - mController.setBackToLauncherCallback(mIOnBackInvokedCallback); - RemoteAnimationTarget animationTarget = createAnimationTarget(); - createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null); triggerBackGesture(); + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); + reset(mIOnBackInvokedCallback); // Simulate transition timeout. mShellExecutor.flushAll(); + mController.onBackAnimationFinished(); + doMotionEvent(MotionEvent.ACTION_DOWN, 0); doMotionEvent(MotionEvent.ACTION_MOVE, 100); + + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); verify(mIOnBackInvokedCallback).onBackStarted(); } @Test public void cancelBackInvokeWhenLostFocus() throws RemoteException { - mController.setBackToLauncherCallback(mIOnBackInvokedCallback); - RemoteAnimationTarget animationTarget = createAnimationTarget(); + mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner); - createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME, null); + createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. doMotionEvent(MotionEvent.ACTION_MOVE, 100); + + simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); verify(mIOnBackInvokedCallback).onBackStarted(); + verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); // Check that back invocation is dispatched. mController.setTriggerBack(true); // Fake trigger back @@ -349,4 +322,14 @@ public class BackAnimationControllerTest extends ShellTestCase { BackEvent.EDGE_LEFT); mEventTime += 10; } + + private void simulateRemoteAnimationStart(int type) throws RemoteException { + RemoteAnimationTarget animationTarget = createAnimationTarget(); + RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget}; + if (mController.mBackAnimationAdapter != null) { + mController.mBackAnimationAdapter.getRunner().onAnimationStart(type, + targets, null, null, mBackAnimationFinishedCallback); + mShellExecutor.flushAll(); + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 83953022dd72d..6acd9b5be0c42 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -227,6 +227,7 @@ import android.view.IWindowFocusObserver; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.WindowManager; +import android.window.BackAnimationAdapter; import android.window.BackNavigationInfo; import android.window.IWindowOrganizerController; import android.window.SplashScreenView.SplashScreenViewParcelable; @@ -1846,14 +1847,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public BackNavigationInfo startBackNavigation(boolean requestAnimation, - IWindowFocusObserver observer) { + public BackNavigationInfo startBackNavigation( + IWindowFocusObserver observer, BackAnimationAdapter adapter) { mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "startBackNavigation()"); if (mBackNavigationController == null) { return null; } - return mBackNavigationController.startBackNavigation(requestAnimation, observer); + + return mBackNavigationController.startBackNavigation(observer, adapter); } /** diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index a3f5401fd375b..d15f954333231 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -16,11 +16,14 @@ package com.android.server.wm; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; + import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.WindowConfiguration; import android.content.ComponentName; import android.graphics.Point; import android.graphics.Rect; @@ -34,7 +37,9 @@ import android.util.Slog; import android.view.IWindowFocusObserver; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.window.BackAnimationAdapter; import android.window.BackNavigationInfo; +import android.window.IBackAnimationFinishedCallback; import android.window.OnBackInvokedCallbackInfo; import android.window.ScreenCapture; import android.window.TaskSnapshot; @@ -43,6 +48,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; +import java.util.ArrayList; + /** * Controller to handle actions related to the back gesture on the server side. */ @@ -72,10 +79,9 @@ class BackNavigationController { */ @VisibleForTesting @Nullable - BackNavigationInfo startBackNavigation(boolean requestAnimation, - IWindowFocusObserver observer) { + BackNavigationInfo startBackNavigation( + IWindowFocusObserver observer, BackAnimationAdapter adapter) { final WindowManagerService wmService = mWindowManagerService; - final SurfaceControl.Transaction tx = wmService.mTransactionFactory.get(); mFocusObserver = observer; int backType = BackNavigationInfo.TYPE_UNDEFINED; @@ -95,18 +101,10 @@ class BackNavigationController { // currentActivity is the last child of currentTask. ActivityRecord prevActivity; WindowContainer removedWindowContainer = null; - SurfaceControl animationLeashParent = null; - HardwareBuffer screenshotBuffer = null; - RemoteAnimationTarget topAppTarget = null; WindowState window; - int prevTaskId; - int prevUserId; - boolean prepareAnimation; - BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder(); synchronized (wmService.mGlobalLock) { - WindowConfiguration taskWindowConfiguration; WindowManagerInternal windowManagerInternal = LocalServices.getService(WindowManagerInternal.class); IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken(); @@ -204,8 +202,8 @@ class BackNavigationController { infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK); final WindowState finalFocusedWindow = window; infoBuilder.setOnBackNavigationDone(new RemoteCallback(result -> - onBackNavigationDone(result, finalFocusedWindow, finalFocusedWindow, - BackNavigationInfo.TYPE_CALLBACK, null, null, false))); + onBackNavigationDone(result, finalFocusedWindow, + BackNavigationInfo.TYPE_CALLBACK))); return infoBuilder.setType(backType).build(); } @@ -245,9 +243,6 @@ class BackNavigationController { } infoBuilder.setType(backType); - prevTaskId = prevTask != null ? prevTask.mTaskId : 0; - prevUserId = prevTask != null ? prevTask.mUserId : 0; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " + "removedContainer:%s, backType=%s", prevActivity != null ? prevActivity.mActivityComponent : null, @@ -256,162 +251,218 @@ class BackNavigationController { BackNavigationInfo.typeToString(backType)); // For now, we only animate when going home. - prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME - && requestAnimation - // Only create a new leash if no leash has been created. - // Otherwise return null for animation target to avoid conflict. - && !removedWindowContainer.hasCommittedReparentToAnimationLeash(); + boolean prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + && adapter != null; + + // 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()) { + Slog.w(TAG, "Can't prepare back animation due to another animation is running."); + prepareAnimation = false; + } if (prepareAnimation) { - taskWindowConfiguration = - currentTask.getTaskInfo().configuration.windowConfiguration; - - infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration); - // Prepare a leash to animate the current top window - // TODO(b/220934562): Use surface animator to better manage animation conflicts. - SurfaceControl animLeash = removedWindowContainer.makeAnimationLeash() - .setName("BackPreview Leash for " + removedWindowContainer) - .setHidden(false) - .setBLASTLayer() - .build(); - removedWindowContainer.reparentSurfaceControl(tx, animLeash); - animationLeashParent = removedWindowContainer.getAnimationLeashParent(); - topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer, - currentActivity, - currentTask, animLeash); - infoBuilder.setDepartingAnimationTarget(topAppTarget); - } - - //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is - // implemented. For now we simply have the mBackScreenshots hash map that dumbly - // saves the screenshots. - if (needsScreenshot(backType) && prevActivity != null - && prevActivity.mActivityComponent != null) { - screenshotBuffer = - getActivitySnapshot(currentTask, prevActivity.mActivityComponent); - } - - // Special handling for back to home animation - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation - && prevTask != null) { - currentTask.mBackGestureStarted = true; - // Make launcher show from behind by marking its top activity as visible and - // launch-behind to bump its visibility for the duration of the back gesture. - prevActivity = prevTask.getTopNonFinishingActivity(); - if (prevActivity != null) { - if (!prevActivity.mVisibleRequested) { - prevActivity.setVisibility(true); - } - prevActivity.mLaunchTaskBehind = true; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to true. Activity=%s", - prevActivity); - prevActivity.mRootWindowContainer.ensureActivitiesVisible( - null /* starting */, 0 /* configChanges */, - false /* preserveWindows */); - } + prepareAnimationIfNeeded(currentTask, prevTask, prevActivity, + removedWindowContainer, backType, adapter); } + infoBuilder.setPrepareRemoteAnimation(prepareAnimation); } // Release wm Lock - // Find a screenshot of the previous activity if we actually have an animation - if (topAppTarget != null && needsScreenshot(backType) && prevTask != null - && screenshotBuffer == null) { - SurfaceControl.Builder builder = new SurfaceControl.Builder() - .setName("BackPreview Screenshot for " + prevActivity) - .setParent(animationLeashParent) - .setHidden(false) - .setBLASTLayer(); - infoBuilder.setScreenshotSurface(builder.build()); - screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId); - infoBuilder.setScreenshotBuffer(screenshotBuffer); - - - // The Animation leash needs to be above the screenshot surface, but the animation leash - // needs to be added before to be in the synchronized block. - tx.setLayer(topAppTarget.leash, 1); - } - WindowContainer finalRemovedWindowContainer = removedWindowContainer; if (finalRemovedWindowContainer != null) { - try { - currentActivity.token.linkToDeath( - () -> resetSurfaces(finalRemovedWindowContainer), 0); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death", e); - resetSurfaces(removedWindowContainer); - return null; - } - - int finalBackType = backType; - final ActivityRecord finalprevActivity = prevActivity; - final Task finalTask = currentTask; + final int finalBackType = backType; final WindowState finalFocusedWindow = window; RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( - result, finalFocusedWindow, finalRemovedWindowContainer, finalBackType, - finalTask, finalprevActivity, prepareAnimation)); + result, finalFocusedWindow, finalBackType)); infoBuilder.setOnBackNavigationDone(onBackNavigationDone); } - tx.apply(); return infoBuilder.build(); } + private void prepareAnimationIfNeeded(Task currentTask, + Task prevTask, ActivityRecord prevActivity, WindowContainer removedWindowContainer, + int backType, BackAnimationAdapter adapter) { + final ArrayList leashes = new ArrayList<>(); + final SurfaceControl.Transaction startedTransaction = currentTask.getPendingTransaction(); + final SurfaceControl.Transaction finishedTransaction = new SurfaceControl.Transaction(); + // Prepare a leash to animate for the departing window + final SurfaceControl animLeash = currentTask.makeAnimationLeash() + .setName("BackPreview Leash for " + currentTask) + .setHidden(false) + .build(); + removedWindowContainer.reparentSurfaceControl(startedTransaction, animLeash); + + final RemoteAnimationTarget topAppTarget = createRemoteAnimationTargetLocked( + currentTask, animLeash, MODE_CLOSING); + + // reset leash after animation finished. + leashes.add(animLeash); + removedWindowContainer.reparentSurfaceControl(finishedTransaction, + removedWindowContainer.getParentSurfaceControl()); + + // Prepare a leash to animate for the entering window. + RemoteAnimationTarget behindAppTarget = null; + if (needsScreenshot(backType)) { + HardwareBuffer screenshotBuffer = null; + switch(backType) { + case BackNavigationInfo.TYPE_CROSS_TASK: + int prevTaskId = prevTask != null ? prevTask.mTaskId : 0; + int prevUserId = prevTask != null ? prevTask.mUserId : 0; + screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId); + break; + case BackNavigationInfo.TYPE_CROSS_ACTIVITY: + //TODO(207481538) Remove once the infrastructure to support per-activity + // screenshot is implemented. For now we simply have the mBackScreenshots hash + // map that dumbly saves the screenshots. + if (prevActivity != null + && prevActivity.mActivityComponent != null) { + screenshotBuffer = + getActivitySnapshot(currentTask, prevActivity.mActivityComponent); + } + break; + } + + // Find a screenshot of the previous activity if we actually have an animation + SurfaceControl animationLeashParent = removedWindowContainer.getAnimationLeashParent(); + if (screenshotBuffer != null) { + final SurfaceControl screenshotSurface = new SurfaceControl.Builder() + .setName("BackPreview Screenshot for " + prevActivity) + .setHidden(false) + .setParent(animationLeashParent) + .setBLASTLayer() + .build(); + startedTransaction.setBuffer(screenshotSurface, screenshotBuffer); + + // The Animation leash needs to be above the screenshot surface, but the animation + // leash needs to be added before to be in the synchronized block. + startedTransaction.setLayer(topAppTarget.leash, 1); + + behindAppTarget = createRemoteAnimationTargetLocked( + prevTask, screenshotSurface, MODE_OPENING); + + // reset leash after animation finished. + leashes.add(screenshotSurface); + } + } else if (prevTask != null) { + // 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 + // and launch-behind to bump its visibility for the duration of the back gesture. + setLaunchBehind(prevActivity); + + final SurfaceControl leash = prevActivity.makeAnimationLeash() + .setName("BackPreview Leash for " + prevActivity) + .setHidden(false) + .build(); + prevActivity.reparentSurfaceControl(startedTransaction, leash); + behindAppTarget = createRemoteAnimationTargetLocked( + prevTask, leash, MODE_OPENING); + + // reset leash after animation finished. + leashes.add(leash); + prevActivity.reparentSurfaceControl(finishedTransaction, + prevActivity.getParentSurfaceControl()); + } + } + + final RemoteAnimationTarget[] targets = (behindAppTarget == null) + ? new RemoteAnimationTarget[] {topAppTarget} + : new RemoteAnimationTarget[] {topAppTarget, behindAppTarget}; + + final ActivityRecord finalPrevActivity = prevActivity; + final IBackAnimationFinishedCallback callback = + new IBackAnimationFinishedCallback.Stub() { + @Override + public void onAnimationFinished(boolean triggerBack) { + for (SurfaceControl sc: leashes) { + finishedTransaction.remove(sc); + } + + 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); + } + } else if (!needsScreenshot(backType)) { + restoreLaunchBehind(finalPrevActivity); + } + } + finishedTransaction.apply(); + } + }; + + startAnimation(backType, targets, adapter, callback); + } + @NonNull private static RemoteAnimationTarget createRemoteAnimationTargetLocked( - WindowContainer removedWindowContainer, - ActivityRecord activityRecord, Task task, SurfaceControl animLeash) { + Task task, SurfaceControl animLeash, int mode) { + ActivityRecord topApp = task.getTopRealVisibleActivity(); + if (topApp == null) { + topApp = task.getTopNonFinishingActivity(); + } + + final WindowState mainWindow = topApp != null + ? topApp.findMainWindow() + : null; + int windowType = INVALID_WINDOW_TYPE; + if (mainWindow != null) { + windowType = mainWindow.getWindowType(); + } + + Rect bounds = new Rect(task.getBounds()); + Rect localBounds = new Rect(bounds); + Point tmpPos = new Point(); + task.getRelativePosition(tmpPos); + localBounds.offsetTo(tmpPos.x, tmpPos.y); + return new RemoteAnimationTarget( task.mTaskId, - RemoteAnimationTarget.MODE_CLOSING, + mode, animLeash, false /* isTransluscent */, new Rect() /* clipRect */, new Rect() /* contentInsets */, - activityRecord.getPrefixOrderIndex(), - new Point(0, 0) /* position */, - new Rect() /* localBounds */, - new Rect() /* screenSpaceBounds */, - removedWindowContainer.getWindowConfiguration(), + task.getPrefixOrderIndex(), + tmpPos /* position */, + localBounds /* localBounds */, + bounds /* screenSpaceBounds */, + task.getWindowConfiguration(), true /* isNotInRecent */, null, null, task.getTaskInfo(), false, - activityRecord.windowType); + windowType); } - private void onBackNavigationDone( - Bundle result, WindowState focusedWindow, WindowContainer windowContainer, - int backType, @Nullable Task task, @Nullable ActivityRecord prevActivity, - boolean prepareAnimation) { - SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); + @VisibleForTesting + void startAnimation(@BackNavigationInfo.BackTargetType int type, + RemoteAnimationTarget[] targets, BackAnimationAdapter backAnimationAdapter, + IBackAnimationFinishedCallback callback) { + mWindowManagerService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { + try { + backAnimationAdapter.getRunner().onAnimationStart(type, + targets, null, null, callback); + } catch (RemoteException e) { + e.printStackTrace(); + } + }); + } + + private void onBackNavigationDone(Bundle result, WindowState focusedWindow, int backType) { boolean triggerBack = result != null && result.getBoolean( BackNavigationInfo.KEY_TRIGGER_BACK); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " - + "task=%s, prevActivity=%s", backType, task, prevActivity); - - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation) { - if (triggerBack) { - if (surfaceControl != null && surfaceControl.isValid()) { - // When going back to home, hide the task surface before it is re-parented to - // avoid flicker. - SurfaceControl.Transaction t = windowContainer.getSyncTransaction(); - t.hide(surfaceControl); - t.apply(); - } - } - if (prevActivity != null && !triggerBack) { - // Restore the launch-behind state. - task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token); - prevActivity.mLaunchTaskBehind = false; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - prevActivity); - } - } else if (task != null) { - task.mBackGestureStarted = false; - } - resetSurfaces(windowContainer); + + "triggerBack=%b", backType, triggerBack); if (mFocusObserver != null) { focusedWindow.unregisterFocusObserver(mFocusObserver); @@ -450,20 +501,47 @@ class BackNavigationController { return true; } - private void resetSurfaces(@NonNull WindowContainer windowContainer) { - synchronized (windowContainer.mWmService.mGlobalLock) { - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Back: Reset surfaces"); - SurfaceControl.Transaction tx = windowContainer.getSyncTransaction(); - SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); - if (surfaceControl != null) { - tx.reparent(surfaceControl, - windowContainer.getParent().getSurfaceControl()); - tx.apply(); - } - } - } - void setWindowManager(WindowManagerService wm) { mWindowManagerService = wm; } + + private void setLaunchBehind(ActivityRecord activity) { + if (activity == null) { + return; + } + if (!activity.mVisibleRequested) { + activity.setVisibility(true); + } + activity.mLaunchTaskBehind = true; + + // Handle fixed rotation launching app. + final DisplayContent dc = activity.mDisplayContent; + dc.rotateInDifferentOrientationIfNeeded(activity); + if (activity.hasFixedRotationTransform()) { + // Set the record so we can recognize it to continue to update display orientation + // if the previous activity becomes the top later. + dc.setFixedRotationLaunchingApp(activity, + activity.getWindowConfiguration().getRotation()); + } + + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); + activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, + 0 /* configChanges */, false /* preserveWindows */, true); + } + + private void restoreLaunchBehind(ActivityRecord activity) { + if (activity == null) { + return; + } + + activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); + + // Restore the launch-behind state. + activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); + activity.mLaunchTaskBehind = false; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to false. Activity=%s", + activity); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index c2ca0a227f26c..7847e7a4dd13f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -40,6 +41,7 @@ import android.hardware.HardwareBuffer; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.WindowManager; +import android.window.BackAnimationAdapter; import android.window.BackEvent; import android.window.BackNavigationInfo; import android.window.IOnBackInvokedCallback; @@ -54,6 +56,7 @@ import com.android.server.LocalServices; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -61,17 +64,18 @@ import java.util.concurrent.TimeUnit; @Presubmit @RunWith(WindowTestRunner.class) public class BackNavigationControllerTests extends WindowTestsBase { - private BackNavigationController mBackNavigationController; private WindowManagerInternal mWindowManagerInternal; + private BackAnimationAdapter mBackAnimationAdapter; @Before public void setUp() throws Exception { - mBackNavigationController = new BackNavigationController(); + mBackNavigationController = Mockito.spy(new BackNavigationController()); LocalServices.removeServiceForTest(WindowManagerInternal.class); mWindowManagerInternal = mock(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); mBackNavigationController.setWindowManager(mWm); + mBackAnimationAdapter = mock(BackAnimationAdapter.class); } @Test @@ -79,14 +83,16 @@ public class BackNavigationControllerTests extends WindowTestsBase { Task task = createTopTaskWithActivity(); IOnBackInvokedCallback callback = withSystemCallback(task); - BackNavigationInfo backNavigationInfo = - mBackNavigationController.startBackNavigation(true, null); + BackNavigationInfo backNavigationInfo = startBackNavigation(); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); - assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull(); - assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull(); assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); + + // verify if back animation would start. + verify(mBackNavigationController).startAnimation( + eq(BackNavigationInfo.TYPE_RETURN_TO_HOME), any(), eq(mBackAnimationAdapter), + any()); } @Test @@ -114,10 +120,6 @@ public class BackNavigationControllerTests extends WindowTestsBase { .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY)); assertWithMessage("Activity callback").that( backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); - - // Until b/207481538 is implemented, this should be null - assertThat(backNavigationInfo.getScreenshotSurface()).isNull(); - assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNull(); } @Test @@ -233,7 +235,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Nullable private BackNavigationInfo startBackNavigation() { - return mBackNavigationController.startBackNavigation(true, null); + return mBackNavigationController.startBackNavigation(null, mBackAnimationAdapter); } @NonNull @@ -287,6 +289,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK; WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, record, "window"); when(record.mSurfaceControl.isValid()).thenReturn(true); + Mockito.doNothing().when(task).reparentSurfaceControl(any(), any()); mAtm.setFocusedTask(task.mTaskId, record); addToWindowMap(window, true); return task; From 197140ab4557a4c82a1bbd4a6570c5b39f05e35d Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Fri, 2 Sep 2022 07:05:43 +0000 Subject: [PATCH 2/3] Add show wallpaper feature for back navigation For the back gesture animation, we want to show the wallpaper but we don't have any wallpaper targer, so we introduce a new flag to set the focused window as wallpaper target if it have to show the wallpaper. Test: Manual with the upcoming CL implementing the cross-task animation Bug: 241808055 Change-Id: I97b27dc009fe0d017083d16f7117f04d93fb2beb --- .../server/wm/ActivityTaskManagerService.java | 2 +- .../server/wm/BackNavigationController.java | 51 +++++++++++++++++-- .../server/wm/RootWindowContainer.java | 5 ++ .../server/wm/WallpaperController.java | 14 ++++- .../wm/BackNavigationControllerTests.java | 2 +- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 6acd9b5be0c42..4ecc62923c5df 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -460,7 +460,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private final ClientLifecycleManager mLifecycleManager; @Nullable - private final BackNavigationController mBackNavigationController; + final BackNavigationController mBackNavigationController; private TaskChangeNotificationController mTaskChangeNotificationController; /** The controller for all operations related to locktask. */ diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d15f954333231..d42ad58f6f217 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -57,6 +57,9 @@ class BackNavigationController { private static final String TAG = "BackNavigationController"; private WindowManagerService mWindowManagerService; private IWindowFocusObserver mFocusObserver; + private boolean mBackAnimationInProgress; + private boolean mShowWallpaper; + private Runnable mPendingAnimation; /** * Returns true if the back predictability feature is enabled @@ -208,6 +211,7 @@ class BackNavigationController { return infoBuilder.setType(backType).build(); } + mBackAnimationInProgress = true; // We don't have an application callback, let's find the destination of the back gesture Task finalTask = currentTask; prevActivity = currentTask.getActivity( @@ -228,6 +232,7 @@ class BackNavigationController { // Our Task should bring back to home removedWindowContainer = currentTask; backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; + mShowWallpaper = true; } else if (currentActivity.isRootOfTask()) { // TODO(208789724): Create single source of truth for this, maybe in // RootWindowContainer @@ -240,6 +245,7 @@ class BackNavigationController { } else { backType = BackNavigationInfo.TYPE_CROSS_TASK; } + mShowWallpaper = true; } infoBuilder.setType(backType); @@ -369,6 +375,13 @@ class BackNavigationController { } } + if (mShowWallpaper) { + currentTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows(); + // TODO(b/241808055): If the current animation need to show wallpaper and animate the + // wallpaper, start the wallpaper animation to collect wallpaper target and deliver it + // to the back animation controller. + } + final RemoteAnimationTarget[] targets = (behindAppTarget == null) ? new RemoteAnimationTarget[] {topAppTarget} : new RemoteAnimationTarget[] {topAppTarget, behindAppTarget}; @@ -399,7 +412,7 @@ class BackNavigationController { } }; - startAnimation(backType, targets, adapter, callback); + scheduleAnimationLocked(backType, targets, adapter, callback); } @NonNull @@ -445,17 +458,38 @@ class BackNavigationController { } @VisibleForTesting - void startAnimation(@BackNavigationInfo.BackTargetType int type, + void scheduleAnimationLocked(@BackNavigationInfo.BackTargetType int type, RemoteAnimationTarget[] targets, BackAnimationAdapter backAnimationAdapter, IBackAnimationFinishedCallback callback) { - mWindowManagerService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { + mPendingAnimation = () -> { try { backAnimationAdapter.getRunner().onAnimationStart(type, targets, null, null, callback); } catch (RemoteException e) { e.printStackTrace(); } - }); + }; + mWindowManagerService.mWindowPlacerLocked.requestTraversal(); + } + + void checkAnimationReady(WallpaperController wallpaperController) { + if (!mBackAnimationInProgress) { + return; + } + + final boolean wallpaperReady = !mShowWallpaper + || (wallpaperController.getWallpaperTarget() != null + && wallpaperController.wallpaperTransitionReady()); + if (wallpaperReady && mPendingAnimation != null) { + startAnimation(); + } + } + + void startAnimation() { + if (mPendingAnimation != null) { + mPendingAnimation.run(); + mPendingAnimation = null; + } } private void onBackNavigationDone(Bundle result, WindowState focusedWindow, int backType) { @@ -468,6 +502,8 @@ class BackNavigationController { focusedWindow.unregisterFocusObserver(mFocusObserver); mFocusObserver = null; } + mBackAnimationInProgress = false; + mShowWallpaper = false; } private HardwareBuffer getActivitySnapshot(@NonNull Task task, @@ -544,4 +580,11 @@ class BackNavigationController { "Setting Activity.mLauncherTaskBehind to false. Activity=%s", activity); } + + boolean isWallpaperVisible(WindowState w) { + if (mBackAnimationInProgress && w.isFocused()) { + return mShowWallpaper; + } + return false; + } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b2ec3f369e9e6..38c75954e6bb8 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -837,6 +837,11 @@ class RootWindowContainer extends WindowContainer if (recentsAnimationController != null) { recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController); } + final BackNavigationController backNavigationController = + mWmService.mAtmService.mBackNavigationController; + if (backNavigationController != null) { + backNavigationController.checkAnimationReady(defaultDisplay.mWallpaperController); + } for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) { final DisplayContent displayContent = mChildren.get(displayNdx); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 0fd3e9b4abae3..81d67952afaef 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -197,7 +197,7 @@ class WallpaperController { && animatingContainer.getAnimation() != null && animatingContainer.getAnimation().getShowWallpaper(); final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper; - if (isRecentsTransitionTarget(w)) { + if (isRecentsTransitionTarget(w) || isBackNavigationTarget(w)) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w); mFindResults.setWallpaperTarget(w); return true; @@ -237,6 +237,12 @@ class WallpaperController { return controller != null && controller.isWallpaperVisible(w); } + private boolean isBackNavigationTarget(WindowState w) { + // The window is in animating by back navigation and set to show wallpaper. + final BackNavigationController controller = mService.mAtmService.mBackNavigationController; + return controller != null && controller.isWallpaperVisible(w); + } + /** * @see #computeLastWallpaperZoomOut() */ @@ -822,6 +828,12 @@ class WallpaperController { if (mService.getRecentsAnimationController() != null) { mService.getRecentsAnimationController().startAnimation(); } + + // If there was a pending back navigation animation that would show wallpaper, start + // the animation due to it was skipped in previous surface placement. + if (mService.mAtmService.mBackNavigationController != null) { + mService.mAtmService.mBackNavigationController.startAnimation(); + } return true; } return false; diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 7847e7a4dd13f..c3d49e1e5152f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -90,7 +90,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); // verify if back animation would start. - verify(mBackNavigationController).startAnimation( + verify(mBackNavigationController).scheduleAnimationLocked( eq(BackNavigationInfo.TYPE_RETURN_TO_HOME), any(), eq(mBackAnimationAdapter), any()); } From 5c85e5b6c09b2a60b14d3c74ee9bd01c7d338f35 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Wed, 17 Aug 2022 06:10:37 +0000 Subject: [PATCH 3/3] 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();