From 8fcc92bb3421e2e27b052ad3d8b957a5ec311ba8 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Tue, 16 Aug 2022 08:17:08 +0000 Subject: [PATCH] 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;