diff options
| author | 2023-05-09 16:01:53 +0000 | |
|---|---|---|
| committer | 2023-05-12 08:20:33 +0000 | |
| commit | 11fccc4afb99a143a2f84668787102995414976a (patch) | |
| tree | fdd5d8ccdf1e0479a5df5be468dfbe072669b1a4 | |
| parent | 3e1928ca21cb4ae402874805acc9de128ee6b08a (diff) | |
Call onBackCancelled() when an active callback is removed from the WindowOnBackInvokedDispatcher
When an OnBackInvokedCallback is unregistered from a WindowOnBackInvokedDispatcher, it is important to check if the callback is the current top one and if the back animation is in progress. If it is, we should call onBackCancelled() on the callback as a final step.
Test: atest WindowOnBackInvokedDispatcherTest
Bug: 276816667
Change-Id: Icaf98899c474c4e8d89d4a9c77acc309d37e9582
3 files changed, 41 insertions, 5 deletions
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java index 618670a0a2c4..e58c044730ae 100644 --- a/core/java/android/window/BackProgressAnimator.java +++ b/core/java/android/window/BackProgressAnimator.java @@ -43,7 +43,7 @@ public class BackProgressAnimator { private ProgressCallback mCallback; private float mProgress = 0; private BackMotionEvent mLastBackEvent; - private boolean mStarted = false; + private boolean mBackAnimationInProgress = false; private void setProgress(float progress) { mProgress = progress; @@ -87,7 +87,7 @@ public class BackProgressAnimator { * @param event the {@link BackMotionEvent} containing the latest target progress. */ public void onBackProgressed(BackMotionEvent event) { - if (!mStarted) { + if (!mBackAnimationInProgress) { return; } mLastBackEvent = event; @@ -108,7 +108,7 @@ public class BackProgressAnimator { reset(); mLastBackEvent = event; mCallback = callback; - mStarted = true; + mBackAnimationInProgress = true; } /** @@ -122,7 +122,7 @@ public class BackProgressAnimator { // Should never happen. mSpring.cancel(); } - mStarted = false; + mBackAnimationInProgress = false; mLastBackEvent = null; mCallback = null; mProgress = 0; @@ -149,8 +149,13 @@ public class BackProgressAnimator { mSpring.animateToFinalPosition(0); } + /** Returns true if the back animation is in progress. */ + boolean isBackAnimationInProgress() { + return mBackAnimationInProgress; + } + private void updateProgressValue(float progress) { - if (mLastBackEvent == null || mCallback == null || !mStarted) { + if (mLastBackEvent == null || mCallback == null || !mBackAnimationInProgress) { return; } mCallback.onProgressUpdate( diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 4d0132e46ba7..22b2ec09c034 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -158,6 +158,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mAllCallbacks.remove(callback); // Re-populate the top callback to WM if the removed callback was previously the top one. if (previousTopCallback == callback) { + // We should call onBackCancelled() when an active callback is removed from dispatcher. + if (mProgressAnimator.isBackAnimationInProgress() + && callback instanceof OnBackAnimationCallback) { + // The ProgressAnimator will handle the new topCallback, so we don't want to call + // onBackCancelled() on it. We call immediately the callback instead. + OnBackAnimationCallback animatedCallback = (OnBackAnimationCallback) callback; + animatedCallback.onBackCancelled(); + Log.d(TAG, "The callback was removed while a back animation was in progress, " + + "an onBackCancelled() was dispatched."); + } setTopOnBackInvokedCallback(getTopCallback()); } } diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index cde100cc20aa..8e772a2d1845 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -173,4 +173,25 @@ public class WindowOnBackInvokedDispatcherTest { waitForIdle(); verify(mCallback2).onBackStarted(any(BackEvent.class)); } + + @Test + public void onUnregisterWhileBackInProgress_callOnBackCancelled() throws RemoteException { + ArgumentCaptor<OnBackInvokedCallbackInfo> captor = + ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class); + + mDispatcher.registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); + + verify(mWindowSession).setOnBackInvokedCallbackInfo( + Mockito.eq(mWindow), + captor.capture()); + IOnBackInvokedCallback iOnBackInvokedCallback = captor.getValue().getCallback(); + iOnBackInvokedCallback.onBackStarted(mBackEvent); + waitForIdle(); + verify(mCallback1).onBackStarted(any(BackEvent.class)); + + mDispatcher.unregisterOnBackInvokedCallback(mCallback1); + verify(mCallback1).onBackCancelled(); + verifyNoMoreInteractions(mCallback1); + } } |