Insets Animation: Add CancellationSignal to controlWindowInsetsAnimation
Fixes an API deficiency where the request to control the animation could not be cancelled until it has succeeded (or failed). To address that, we return a CancellationSignal. Bug: 143556682 Test: make checkapi; atest InsetsControllerTest Change-Id: I03d1bcc7013c10d2deac7b1059b99894b7103230
This commit is contained in:
@@ -55131,7 +55131,7 @@ package android.view {
|
||||
}
|
||||
|
||||
public interface WindowInsetsController {
|
||||
method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
|
||||
method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
|
||||
method public int getSystemBarsAppearance();
|
||||
method public int getSystemBarsBehavior();
|
||||
method public void hide(int);
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArraySet;
|
||||
@@ -39,7 +40,6 @@ import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.Property;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InputDevice.MotionRange;
|
||||
import android.view.InsetsSourceConsumer.ShowResult;
|
||||
import android.view.InsetsState.InternalInsetsType;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
@@ -242,13 +242,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
|
||||
long durationMs, Interpolator interpolator, @AnimationType int animationType,
|
||||
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
|
||||
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
|
||||
CancellationSignal cancellationSignal) {
|
||||
this.types = types;
|
||||
this.listener = listener;
|
||||
this.durationMs = durationMs;
|
||||
this.interpolator = interpolator;
|
||||
this.animationType = animationType;
|
||||
this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
|
||||
this.cancellationSignal = cancellationSignal;
|
||||
}
|
||||
|
||||
final @InsetsType int types;
|
||||
@@ -257,6 +259,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
final Interpolator interpolator;
|
||||
final @AnimationType int animationType;
|
||||
final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
|
||||
final CancellationSignal cancellationSignal;
|
||||
}
|
||||
|
||||
private final String TAG = "InsetsControllerImpl";
|
||||
@@ -455,10 +458,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
PendingControlRequest pendingRequest = mPendingImeControlRequest;
|
||||
mPendingImeControlRequest = null;
|
||||
mHandler.removeCallbacks(mPendingControlTimeout);
|
||||
controlAnimationUnchecked(pendingRequest.types, pendingRequest.listener, mFrame,
|
||||
CancellationSignal cancellationSignal = controlAnimationUnchecked(
|
||||
pendingRequest.types,
|
||||
pendingRequest.listener, mFrame,
|
||||
true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
|
||||
false /* fade */, pendingRequest.animationType,
|
||||
pendingRequest.layoutInsetsDuringAnimation);
|
||||
pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -504,35 +510,39 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
|
||||
public CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
|
||||
@Nullable Interpolator interpolator,
|
||||
@NonNull WindowInsetsAnimationControlListener listener) {
|
||||
controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, interpolator,
|
||||
ANIMATION_TYPE_USER);
|
||||
return controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
|
||||
interpolator, ANIMATION_TYPE_USER);
|
||||
}
|
||||
|
||||
private void controlWindowInsetsAnimation(@InsetsType int types,
|
||||
private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
|
||||
WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
|
||||
@Nullable Interpolator interpolator, @AnimationType int animationType) {
|
||||
// If the frame of our window doesn't span the entire display, the control API makes very
|
||||
// little sense, as we don't deal with negative insets. So just cancel immediately.
|
||||
if (!mState.getDisplayFrame().equals(mFrame)) {
|
||||
listener.onCancelled();
|
||||
return;
|
||||
CancellationSignal cancellationSignal = new CancellationSignal();
|
||||
cancellationSignal.cancel();
|
||||
return cancellationSignal;
|
||||
}
|
||||
controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
|
||||
return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
|
||||
false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
|
||||
}
|
||||
|
||||
private void controlAnimationUnchecked(@InsetsType int types,
|
||||
private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
|
||||
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
|
||||
long durationMs, Interpolator interpolator, boolean fade,
|
||||
@AnimationType int animationType,
|
||||
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
|
||||
CancellationSignal cancellationSignal = new CancellationSignal();
|
||||
if (types == 0) {
|
||||
// nothing to animate.
|
||||
listener.onCancelled();
|
||||
return;
|
||||
cancellationSignal.cancel();
|
||||
return cancellationSignal;
|
||||
}
|
||||
cancelExistingControllers(types);
|
||||
|
||||
@@ -546,21 +556,31 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (!imeReady) {
|
||||
// IME isn't ready, all requested types will be animated once IME is ready
|
||||
abortPendingImeControlRequest();
|
||||
mPendingImeControlRequest = new PendingControlRequest(types, listener, durationMs,
|
||||
interpolator, animationType, layoutInsetsDuringAnimation);
|
||||
final PendingControlRequest request = new PendingControlRequest(types,
|
||||
listener, durationMs,
|
||||
interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal);
|
||||
mPendingImeControlRequest = request;
|
||||
mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
|
||||
return;
|
||||
cancellationSignal.setOnCancelListener(() -> {
|
||||
if (mPendingImeControlRequest == request) {
|
||||
abortPendingImeControlRequest();
|
||||
}
|
||||
});
|
||||
return cancellationSignal;
|
||||
}
|
||||
|
||||
if (typesReady == 0) {
|
||||
listener.onCancelled();
|
||||
return;
|
||||
cancellationSignal.cancel();
|
||||
return cancellationSignal;
|
||||
}
|
||||
|
||||
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
|
||||
frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
|
||||
layoutInsetsDuringAnimation);
|
||||
mRunningAnimations.add(new RunningAnimation(controller, animationType));
|
||||
cancellationSignal.setOnCancelListener(controller::onCancelled);
|
||||
return cancellationSignal;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Insets;
|
||||
import android.os.CancellationSignal;
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
|
||||
import android.view.animation.Interpolator;
|
||||
@@ -156,13 +157,15 @@ public interface WindowInsetsController {
|
||||
* {@link InsetsAnimation#getInterpolatedFraction()}.
|
||||
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
|
||||
* windows are ready to be controlled, among other callbacks.
|
||||
*
|
||||
* @return A cancellation signal that the caller can use to cancel the request to obtain
|
||||
* control, or once they have control, to cancel the control.
|
||||
* @see InsetsAnimation#getFraction()
|
||||
* @see InsetsAnimation#getInterpolatedFraction()
|
||||
* @see InsetsAnimation#getInterpolator()
|
||||
* @see InsetsAnimation#getDurationMillis()
|
||||
*/
|
||||
void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
|
||||
@NonNull
|
||||
CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
|
||||
@Nullable Interpolator interpolator,
|
||||
@NonNull WindowInsetsAnimationControlListener listener);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.CancellationSignal;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.WindowInsets.Type;
|
||||
@@ -515,6 +516,34 @@ public class InsetsControllerTest {
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancellation_afterGainingControl() throws Exception {
|
||||
InsetsSourceControl control =
|
||||
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
|
||||
mController.onControlsChanged(new InsetsSourceControl[] { control });
|
||||
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
WindowInsetsAnimationControlListener mockListener =
|
||||
mock(WindowInsetsAnimationControlListener.class);
|
||||
CancellationSignal cancellationSignal = mController.controlWindowInsetsAnimation(
|
||||
statusBars(), 0 /* durationMs */,
|
||||
new LinearInterpolator(), mockListener);
|
||||
|
||||
// Ready gets deferred until next predraw
|
||||
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
|
||||
|
||||
verify(mockListener).onReady(any(), anyInt());
|
||||
|
||||
cancellationSignal.cancel();
|
||||
verify(mockListener).onCancelled();
|
||||
});
|
||||
waitUntilNextFrame();
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
|
||||
});
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlImeNotReady() {
|
||||
prepareControls();
|
||||
@@ -580,6 +609,28 @@ public class InsetsControllerTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlImeNotReady_cancel() {
|
||||
prepareControls();
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
WindowInsetsAnimationControlListener listener =
|
||||
mock(WindowInsetsAnimationControlListener.class);
|
||||
mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener)
|
||||
.cancel();
|
||||
|
||||
verify(listener).onCancelled();
|
||||
|
||||
// Ready gets deferred until next predraw
|
||||
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
|
||||
|
||||
verify(listener, never()).onReady(any(), anyInt());
|
||||
|
||||
// Pretend that timeout is happening
|
||||
mTestClock.fastForward(2500);
|
||||
mTestHandler.timeAdvance();
|
||||
});
|
||||
}
|
||||
|
||||
private void waitUntilNextFrame() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
|
||||
|
||||
Reference in New Issue
Block a user