Merge "Cancel the back animation if the topCallback is removed or the WindowOnBackInvokedDispatcher is detached from the window." into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b3805c735f
@@ -163,19 +163,25 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
// Re-populate the top callback to WM if the removed callback was previously the top one.
|
// Re-populate the top callback to WM if the removed callback was previously the top one.
|
||||||
if (previousTopCallback == callback) {
|
if (previousTopCallback == callback) {
|
||||||
// We should call onBackCancelled() when an active callback is removed from dispatcher.
|
// We should call onBackCancelled() when an active callback is removed from dispatcher.
|
||||||
if (mProgressAnimator.isBackAnimationInProgress()
|
sendCancelledIfInProgress(callback);
|
||||||
&& 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());
|
setTopOnBackInvokedCallback(getTopCallback());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendCancelledIfInProgress(@NonNull OnBackInvokedCallback callback) {
|
||||||
|
boolean isInProgress = mProgressAnimator.isBackAnimationInProgress();
|
||||||
|
if (isInProgress && callback instanceof OnBackAnimationCallback) {
|
||||||
|
OnBackAnimationCallback animatedCallback = (OnBackAnimationCallback) callback;
|
||||||
|
animatedCallback.onBackCancelled();
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "sendCancelIfRunning: callback canceled");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "sendCancelIfRunning: isInProgress=" + isInProgress
|
||||||
|
+ "callback=" + callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
|
public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
|
||||||
registerOnBackInvokedCallbackUnchecked(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
|
registerOnBackInvokedCallbackUnchecked(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
|
||||||
@@ -188,9 +194,20 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
mImeDispatcher = null;
|
mImeDispatcher = null;
|
||||||
}
|
}
|
||||||
if (!mAllCallbacks.isEmpty()) {
|
if (!mAllCallbacks.isEmpty()) {
|
||||||
|
OnBackInvokedCallback topCallback = getTopCallback();
|
||||||
|
if (topCallback != null) {
|
||||||
|
sendCancelledIfInProgress(topCallback);
|
||||||
|
} else {
|
||||||
|
// Should not be possible
|
||||||
|
Log.e(TAG, "There is no topCallback, even if mAllCallbacks is not empty");
|
||||||
|
}
|
||||||
// Clear binder references in WM.
|
// Clear binder references in WM.
|
||||||
setTopOnBackInvokedCallback(null);
|
setTopOnBackInvokedCallback(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should also stop running animations since all callbacks have been removed.
|
||||||
|
// note: mSpring.skipToEnd(), in ProgressAnimator.reset(), requires the main handler.
|
||||||
|
Handler.getMain().post(mProgressAnimator::reset);
|
||||||
mAllCallbacks.clear();
|
mAllCallbacks.clear();
|
||||||
mOnBackInvokedCallbacks.clear();
|
mOnBackInvokedCallbacks.clear();
|
||||||
}
|
}
|
||||||
@@ -342,12 +359,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
@Override
|
@Override
|
||||||
public void onBackInvoked() throws RemoteException {
|
public void onBackInvoked() throws RemoteException {
|
||||||
Handler.getMain().post(() -> {
|
Handler.getMain().post(() -> {
|
||||||
|
boolean isInProgress = mProgressAnimator.isBackAnimationInProgress();
|
||||||
mProgressAnimator.reset();
|
mProgressAnimator.reset();
|
||||||
final OnBackInvokedCallback callback = mCallbackRef.get();
|
final OnBackInvokedCallback callback = mCallbackRef.get();
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference.");
|
Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (callback instanceof OnBackAnimationCallback && !isInProgress) {
|
||||||
|
Log.w(TAG, "ProgressAnimator was not in progress, skip onBackInvoked().");
|
||||||
|
return;
|
||||||
|
}
|
||||||
callback.onBackInvoked();
|
callback.onBackInvoked();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.isNull;
|
|||||||
import static org.mockito.Mockito.atLeast;
|
import static org.mockito.Mockito.atLeast;
|
||||||
import static org.mockito.Mockito.atMost;
|
import static org.mockito.Mockito.atMost;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
@@ -340,4 +341,42 @@ public class WindowOnBackInvokedDispatcherTest {
|
|||||||
verify(mCallback1).onBackCancelled();
|
verify(mCallback1).onBackCancelled();
|
||||||
verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull());
|
verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBackInvoked_calledAfterOnBackStarted() throws RemoteException {
|
||||||
|
mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
|
||||||
|
OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
|
||||||
|
|
||||||
|
callbackInfo.getCallback().onBackStarted(mBackEvent);
|
||||||
|
|
||||||
|
waitForIdle();
|
||||||
|
verify(mCallback1).onBackStarted(any(BackEvent.class));
|
||||||
|
|
||||||
|
callbackInfo.getCallback().onBackInvoked();
|
||||||
|
|
||||||
|
waitForIdle();
|
||||||
|
verify(mCallback1).onBackInvoked();
|
||||||
|
verify(mCallback1, never()).onBackCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDetachFromWindow_cancelCallbackAndIgnoreOnBackInvoked() throws RemoteException {
|
||||||
|
mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
|
||||||
|
|
||||||
|
OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
|
||||||
|
|
||||||
|
callbackInfo.getCallback().onBackStarted(mBackEvent);
|
||||||
|
|
||||||
|
waitForIdle();
|
||||||
|
verify(mCallback1).onBackStarted(any(BackEvent.class));
|
||||||
|
|
||||||
|
// This should trigger mCallback1.onBackCancelled()
|
||||||
|
mDispatcher.detachFromWindow();
|
||||||
|
// This should be ignored by mCallback1
|
||||||
|
callbackInfo.getCallback().onBackInvoked();
|
||||||
|
|
||||||
|
waitForIdle();
|
||||||
|
verify(mCallback1, never()).onBackInvoked();
|
||||||
|
verify(mCallback1).onBackCancelled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user