From 47e45086dbd3a9e684b64186ded31e2850651cf0 Mon Sep 17 00:00:00 2001 From: omarmt Date: Tue, 4 Apr 2023 12:47:00 +0000 Subject: [PATCH] Add isAnimationCallback() in BackNavigationInfo This method returns if the callback is an OnBackAnimationCallback. This value can be serialized and can be used to determine whether or not to run predictive animations when you have no way to check the callback directly, for example in the BackAnimationController you only have access to the IOnBackInvokedCallback. Why? OnBackAnimationCallback is used by apps that want to play a custom animation when the user swipes back. OnBackAnimationCallback exposes BackEvent every time there is a new progress value. Ideally, we would like to expose the velocity only on the last progress value (or onBackInvoked()), but to do that we have to change the API. We are unable to do so now. Therefore, we decided to handle the fling gesture in the system instead. As a result, we now need to determine if the app supports OnBackAnimationCallback so that we only send the fling if we have an OnBackAnimationCallback registered. BackAnimationController can now produce more back events. This is done by producing more events when the user lifts their finger. This way, the developer does not have to handle the fling gesture. This approach was chosen because the velocity cannot be exposed in the BackEvent. Test: atest BackNavigationControllerTests Bug: 263402927 Change-Id: I4d85253c9ade39f35ef0d8c70c6b1c7c31b1390d --- .../android/window/BackNavigationInfo.java | 36 +++++++++++++--- .../window/OnBackInvokedCallbackInfo.java | 17 +++++++- .../window/WindowOnBackInvokedDispatcher.java | 5 ++- .../server/wm/BackNavigationController.java | 1 + .../wm/BackNavigationControllerTests.java | 42 +++++++++++++++++-- 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index e0ee68337061b..e44f43609256a 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -94,26 +94,29 @@ public final class BackNavigationInfo implements Parcelable { @Nullable private final IOnBackInvokedCallback mOnBackInvokedCallback; private final boolean mPrepareRemoteAnimation; + private final boolean mAnimationCallback; @Nullable private final CustomAnimationInfo mCustomAnimationInfo; /** * Create a new {@link BackNavigationInfo} instance. * - * @param type The {@link BackTargetType} of the destination (what will be - * @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 type The {@link BackTargetType} of the destination (what will be + * @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. */ private BackNavigationInfo(@BackTargetType int type, @Nullable RemoteCallback onBackNavigationDone, @Nullable IOnBackInvokedCallback onBackInvokedCallback, boolean isPrepareRemoteAnimation, + boolean isAnimationCallback, @Nullable CustomAnimationInfo customAnimationInfo) { mType = type; mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; mPrepareRemoteAnimation = isPrepareRemoteAnimation; + mAnimationCallback = isAnimationCallback; mCustomAnimationInfo = customAnimationInfo; } @@ -122,6 +125,7 @@ public final class BackNavigationInfo implements Parcelable { mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR); mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); mPrepareRemoteAnimation = in.readBoolean(); + mAnimationCallback = in.readBoolean(); mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR); } @@ -132,6 +136,7 @@ public final class BackNavigationInfo implements Parcelable { dest.writeTypedObject(mOnBackNavigationDone, flags); dest.writeStrongInterface(mOnBackInvokedCallback); dest.writeBoolean(mPrepareRemoteAnimation); + dest.writeBoolean(mAnimationCallback); dest.writeTypedObject(mCustomAnimationInfo, flags); } @@ -159,13 +164,21 @@ public final class BackNavigationInfo implements Parcelable { } /** - * Return true if the core is preparing a back gesture nimation. + * Return true if the core is preparing a back gesture animation. * @hide */ public boolean isPrepareRemoteAnimation() { return mPrepareRemoteAnimation; } + /** + * Return true if the callback is {@link OnBackAnimationCallback}. + * @hide + */ + public boolean isAnimationCallback() { + return mAnimationCallback; + } + /** * 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. @@ -214,6 +227,8 @@ public final class BackNavigationInfo implements Parcelable { + "mType=" + typeToString(mType) + " (" + mType + ")" + ", mOnBackNavigationDone=" + mOnBackNavigationDone + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback + + ", mPrepareRemoteAnimation=" + mPrepareRemoteAnimation + + ", mAnimationCallback=" + mAnimationCallback + ", mCustomizeAnimationInfo=" + mCustomAnimationInfo + '}'; } @@ -343,6 +358,7 @@ public final class BackNavigationInfo implements Parcelable { private IOnBackInvokedCallback mOnBackInvokedCallback = null; private boolean mPrepareRemoteAnimation; private CustomAnimationInfo mCustomAnimationInfo; + private boolean mAnimationCallback = false; /** * @see BackNavigationInfo#getType() @@ -387,6 +403,7 @@ public final class BackNavigationInfo implements Parcelable { mCustomAnimationInfo.mWindowAnimations = windowAnimations; return this; } + /** * Set resources ids for customize activity animation. */ @@ -401,6 +418,14 @@ public final class BackNavigationInfo implements Parcelable { return this; } + /** + * @param isAnimationCallback whether the callback is {@link OnBackAnimationCallback} + */ + public Builder setAnimationCallback(boolean isAnimationCallback) { + mAnimationCallback = isAnimationCallback; + return this; + } + /** * Builds and returns an instance of {@link BackNavigationInfo} */ @@ -408,6 +433,7 @@ public final class BackNavigationInfo implements Parcelable { return new BackNavigationInfo(mType, mOnBackNavigationDone, mOnBackInvokedCallback, mPrepareRemoteAnimation, + mAnimationCallback, mCustomAnimationInfo); } } diff --git a/core/java/android/window/OnBackInvokedCallbackInfo.java b/core/java/android/window/OnBackInvokedCallbackInfo.java index 6480da336590a..bb5fe96fdec1b 100644 --- a/core/java/android/window/OnBackInvokedCallbackInfo.java +++ b/core/java/android/window/OnBackInvokedCallbackInfo.java @@ -28,15 +28,20 @@ public final class OnBackInvokedCallbackInfo implements Parcelable { @NonNull private final IOnBackInvokedCallback mCallback; private @OnBackInvokedDispatcher.Priority int mPriority; + private final boolean mIsAnimationCallback; - public OnBackInvokedCallbackInfo(@NonNull IOnBackInvokedCallback callback, int priority) { + public OnBackInvokedCallbackInfo(@NonNull IOnBackInvokedCallback callback, + int priority, + boolean isAnimationCallback) { mCallback = callback; mPriority = priority; + mIsAnimationCallback = isAnimationCallback; } private OnBackInvokedCallbackInfo(@NonNull Parcel in) { mCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); mPriority = in.readInt(); + mIsAnimationCallback = in.readBoolean(); } @Override @@ -48,6 +53,7 @@ public final class OnBackInvokedCallbackInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStrongInterface(mCallback); dest.writeInt(mPriority); + dest.writeBoolean(mIsAnimationCallback); } public static final Creator CREATOR = @@ -77,9 +83,16 @@ public final class OnBackInvokedCallbackInfo implements Parcelable { return mPriority; } + public boolean isAnimationCallback() { + return mIsAnimationCallback; + } + @Override public String toString() { return "OnBackInvokedCallbackInfo{" - + "mCallback=" + mCallback + ", mPriority=" + mPriority + '}'; + + "mCallback=" + mCallback + + ", mPriority=" + mPriority + + ", mIsAnimationCallback=" + mIsAnimationCallback + + '}'; } } diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 8066f5085a01b..51382a4b265f4 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -193,7 +193,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) callback).getIOnBackInvokedCallback() : new OnBackInvokedCallbackWrapper(callback); - callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority); + callbackInfo = new OnBackInvokedCallbackInfo( + iCallback, + priority, + callback instanceof OnBackAnimationCallback); } mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 745374301263c..ba2eff4e79c6e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -227,6 +227,7 @@ class BackNavigationController { backType = BackNavigationInfo.TYPE_CALLBACK; } infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); + infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback()); mNavigationMonitor.startMonitor(window, navigationObserver); } 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 17ae215c29303..6d7f2c13197c6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -232,11 +232,36 @@ public class BackNavigationControllerTests extends WindowTestsBase { IOnBackInvokedCallback callback = createOnBackInvokedCallback(); window.setOnBackInvokedCallbackInfo( - new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT)); + new OnBackInvokedCallbackInfo( + callback, + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + /* isAnimationCallback = */ false)); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); assertThat(backNavigationInfo.getType()).isEqualTo(BackNavigationInfo.TYPE_CALLBACK); + assertThat(backNavigationInfo.isAnimationCallback()).isEqualTo(false); + assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); + } + + @Test + public void backInfoWithAnimationCallback() { + WindowState window = createWindow(null, WindowManager.LayoutParams.TYPE_WALLPAPER, + "Wallpaper"); + addToWindowMap(window, true); + makeWindowVisibleAndDrawn(window); + + IOnBackInvokedCallback callback = createOnBackInvokedCallback(); + window.setOnBackInvokedCallbackInfo( + new OnBackInvokedCallbackInfo( + callback, + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + /* isAnimationCallback = */ true)); + + BackNavigationInfo backNavigationInfo = startBackNavigation(); + assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); + assertThat(backNavigationInfo.getType()).isEqualTo(BackNavigationInfo.TYPE_CALLBACK); + assertThat(backNavigationInfo.isAnimationCallback()).isEqualTo(true); assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); } @@ -364,7 +389,10 @@ public class BackNavigationControllerTests extends WindowTestsBase { IOnBackInvokedCallback callback = createOnBackInvokedCallback(); window.setOnBackInvokedCallbackInfo( - new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT)); + new OnBackInvokedCallbackInfo( + callback, + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + /* isAnimationCallback = */ false)); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertThat(backNavigationInfo).isNull(); @@ -450,14 +478,20 @@ public class BackNavigationControllerTests extends WindowTestsBase { private IOnBackInvokedCallback withSystemCallback(Task task) { IOnBackInvokedCallback callback = createOnBackInvokedCallback(); task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo( - new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM)); + new OnBackInvokedCallbackInfo( + callback, + OnBackInvokedDispatcher.PRIORITY_SYSTEM, + /* isAnimationCallback = */ false)); return callback; } private IOnBackInvokedCallback withAppCallback(Task task) { IOnBackInvokedCallback callback = createOnBackInvokedCallback(); task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo( - new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT)); + new OnBackInvokedCallbackInfo( + callback, + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + /* isAnimationCallback = */ false)); return callback; }