diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java index 618670a0a2c4c..e58c044730ae9 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 4d0132e46ba7b..22b2ec09c034a 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 cde100cc20aaa..8e772a2d18459 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 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); + } }