WindowInsetsAnimationController: Add state callback and getters

Adds more comprehensive callbacks and getters for the WindowInsetsAnimationController,
to make it more straight forward to properly use.

Test: atest InsetsControllerTest PendingInsetsControllerTest
Fixes: 151707442
Change-Id: Ida55f609112396c0f6de4c5c4431e0793c2e315e
diff --git a/api/current.txt b/api/current.txt
index 826bbbc..4b79621 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -55574,7 +55574,8 @@
   }
 
   public interface WindowInsetsAnimationControlListener {
-    method public void onCancelled();
+    method public void onCancelled(@Nullable android.view.WindowInsetsAnimationController);
+    method public void onFinished(@NonNull android.view.WindowInsetsAnimationController);
     method public void onReady(@NonNull android.view.WindowInsetsAnimationController, int);
   }
 
@@ -55586,6 +55587,9 @@
     method @NonNull public android.graphics.Insets getHiddenStateInsets();
     method @NonNull public android.graphics.Insets getShownStateInsets();
     method public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public default boolean isReady();
     method public void setInsetsAndAlpha(@Nullable android.graphics.Insets, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
   }
 
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index baee412..d2e6036 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -200,6 +200,7 @@
         }
         setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);
         mFinished = true;
+        mListener.onFinished(this);
 
         mShownOnFinish = shown;
     }
@@ -216,11 +217,17 @@
             return;
         }
         mCancelled = true;
-        mListener.onCancelled();
+        mListener.onCancelled(this);
 
         releaseLeashes();
     }
 
+    @Override
+    public boolean isFinished() {
+        return mFinished;
+    }
+
+    @Override
     public boolean isCancelled() {
         return mCancelled;
     }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 123b9db..96f7340 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -213,7 +213,11 @@
         }
 
         @Override
-        public void onCancelled() {
+        public void onFinished(WindowInsetsAnimationController controller) {
+        }
+
+        @Override
+        public void onCancelled(WindowInsetsAnimationController controller) {
             // Animator can be null when it is cancelled before onReady() completes.
             if (mAnimator != null) {
                 mAnimator.cancel();
@@ -583,7 +587,7 @@
             boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
             @AnimationType int animationType) {
         if (!checkDisplayFramesForControlling()) {
-            listener.onCancelled();
+            listener.onCancelled(null);
             return;
         }
         controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
@@ -608,7 +612,7 @@
             boolean useInsetsAnimationThread) {
         if (types == 0) {
             // nothing to animate.
-            listener.onCancelled();
+            listener.onCancelled(null);
             return;
         }
         cancelExistingControllers(types);
@@ -641,7 +645,7 @@
         }
 
         if (typesReady == 0) {
-            listener.onCancelled();
+            listener.onCancelled(null);
             return;
         }
 
@@ -756,7 +760,7 @@
 
     private void abortPendingImeControlRequest() {
         if (mPendingImeControlRequest != null) {
-            mPendingImeControlRequest.listener.onCancelled();
+            mPendingImeControlRequest.listener.onCancelled(null);
             mPendingImeControlRequest = null;
             mHandler.removeCallbacks(mPendingControlTimeout);
         }
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index e8d9bb5..229ee03 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -172,7 +172,7 @@
             mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
                     interpolator, cancellationSignal, listener);
         } else {
-            listener.onCancelled();
+            listener.onCancelled(null);
         }
     }
 
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index faaf920..b61fa36 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -17,21 +17,31 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.inputmethod.EditorInfo;
 
 /**
- * Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
+ * Listener that encapsulates a request to
+ * {@link WindowInsetsController#controlWindowInsetsAnimation}.
+ *
+ * <p>
+ * Insets can be controlled with the supplied {@link WindowInsetsAnimationController} from
+ * {@link #onReady} until either {@link #onFinished} or {@link #onCancelled}.
+ *
+ * <p>
+ * Once the control over insets is finished or cancelled, it will not be regained until a new
+ * request to {@link WindowInsetsController#controlWindowInsetsAnimation} is made.
+ *
+ * <p>
+ * The request to control insets can fail immediately. In that case {@link #onCancelled} will be
+ * invoked without a preceding {@link #onReady}.
+ *
+ * @see WindowInsetsController#controlWindowInsetsAnimation
  */
 public interface WindowInsetsAnimationControlListener {
 
     /**
-     * @hide
-     */
-    default void onPrepare(int types) {
-    }
-
-    /**
      * Called when the animation is ready to be controlled. This may be delayed when the IME needs
      * to redraw because of an {@link EditorInfo} change, or when the window is starting up.
      *
@@ -41,14 +51,40 @@
      *              {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
      *              wasn't able to gain the controls because it wasn't the IME target or not
      *              currently the window that's controlling the system bars.
+     * @see WindowInsetsAnimationController#isReady
      */
     void onReady(@NonNull WindowInsetsAnimationController controller, @InsetsType int types);
 
     /**
-     * Called when the window no longer has control over the requested types. If it loses control
-     * over one type, the whole control will be cancelled. If none of the requested types were
-     * available when requesting the control, the animation control will be cancelled immediately
-     * without {@link #onReady} being called.
+     * Called when the request for control over the insets has
+     * {@link WindowInsetsAnimationController#finish finished}.
+     *
+     * Once this callback is invoked, the supplied {@link WindowInsetsAnimationController}
+     * is no longer {@link WindowInsetsAnimationController#isReady() ready}.
+     *
+     * Control will not be regained until a new request
+     * to {@link WindowInsetsController#controlWindowInsetsAnimation} is made.
+     *
+     * @param controller the controller which has finished.
+     * @see WindowInsetsAnimationController#isFinished
      */
-    void onCancelled();
+    void onFinished(@NonNull WindowInsetsAnimationController controller);
+
+    /**
+     * Called when the request for control over the insets has been cancelled, either
+     * because the {@link android.os.CancellationSignal} associated with the
+     * {@link WindowInsetsController#controlWindowInsetsAnimation request} has been invoked, or
+     * the window has lost control over the insets (e.g. because it lost focus).
+     *
+     * Once this callback is invoked, the supplied {@link WindowInsetsAnimationController}
+     * is no longer {@link WindowInsetsAnimationController#isReady() ready}.
+     *
+     * Control will not be regained until a new request
+     * to {@link WindowInsetsController#controlWindowInsetsAnimation} is made.
+     *
+     * @param controller the controller which has been cancelled, or null if the request
+     *                   was cancelled before {@link #onReady} was invoked.
+     * @see WindowInsetsAnimationController#isCancelled
+     */
+    void onCancelled(@Nullable WindowInsetsAnimationController controller);
 }
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 2c7880b..c191a54 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -139,10 +139,43 @@
             @FloatRange(from = 0f, to = 1f) float fraction);
 
     /**
-     * Finishes the animation, and leaves the windows shown or hidden. After invoking
-     * {@link #finish(boolean)}, this instance is no longer valid.
+     * Finishes the animation, and leaves the windows shown or hidden.
+     * <p>
+     * After invoking {@link #finish(boolean)}, this instance is no longer {@link #isReady ready}.
+     *
      * @param shown if {@code true}, the windows will be shown after finishing the
      *              animation. Otherwise they will be hidden.
      */
     void finish(boolean shown);
+
+    /**
+     * Returns whether this instance is ready to be used to control window insets.
+     * <p>
+     * Instances are ready when passed in {@link WindowInsetsAnimationControlListener#onReady}
+     * and stop being ready when it is either {@link #isFinished() finished} or
+     * {@link #isCancelled() cancelled}.
+     *
+     * @return {@code true} if the instance is ready, {@code false} otherwise.
+     */
+    default boolean isReady() {
+        return !isFinished() && !isCancelled();
+    }
+
+    /**
+     * Returns whether this instance has been finished by a call to {@link #finish}.
+     *
+     * @see WindowInsetsAnimationControlListener#onFinished
+     * @return {@code true} if the instance is finished, {@code false} otherwise.
+     */
+    boolean isFinished();
+
+    /**
+     * Returns whether this instance has been cancelled by the system, or by invoking the
+     * {@link android.os.CancellationSignal} passed into
+     * {@link WindowInsetsController#controlWindowInsetsAnimation}.
+     *
+     * @see WindowInsetsAnimationControlListener#onCancelled
+     * @return {@code true} if the instance is cancelled, {@code false} otherwise.
+     */
+    boolean isCancelled();
 }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index fe25e79..22b4e45 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -188,7 +188,7 @@
             fail("Expected exception to be thrown");
         } catch (IllegalStateException ignored) {
         }
-        verify(mMockListener).onCancelled();
+        verify(mMockListener).onCancelled(mController);
         mController.finish(true /* shown */);
     }
 
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 42ab2e7..90a62e7 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -204,7 +204,7 @@
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
             verify(mockListener).onReady(any(), anyInt());
             mController.onControlsChanged(new InsetsSourceControl[0]);
-            verify(mockListener).onCancelled();
+            verify(mockListener).onCancelled(notNull());
         });
     }
 
@@ -221,7 +221,7 @@
                 new CancellationSignal(), controlListener);
         mController.addOnControllableInsetsChangedListener(
                 (controller, typeMask) -> assertEquals(0, typeMask));
-        verify(controlListener).onCancelled();
+        verify(controlListener).onCancelled(null);
         verify(controlListener, never()).onReady(any(), anyInt());
     }
 
@@ -533,7 +533,7 @@
             verify(mockListener).onReady(any(), anyInt());
 
             cancellationSignal.cancel();
-            verify(mockListener).onCancelled();
+            verify(mockListener).onCancelled(notNull());
         });
         waitUntilNextFrame();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -584,7 +584,7 @@
             // Pretend that we are losing control
             mController.onControlsChanged(new InsetsSourceControl[0]);
 
-            verify(listener).onCancelled();
+            verify(listener).onCancelled(null);
         });
     }
 
@@ -606,7 +606,7 @@
             mTestClock.fastForward(2500);
             mTestHandler.timeAdvance();
 
-            verify(listener).onCancelled();
+            verify(listener).onCancelled(null);
         });
     }
 
@@ -621,7 +621,7 @@
                     cancellationSignal, listener);
             cancellationSignal.cancel();
 
-            verify(listener).onCancelled();
+            verify(listener).onCancelled(null);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 33f859e..03c8b1b 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -99,7 +99,7 @@
         CancellationSignal cancellationSignal = new CancellationSignal();
         mPendingInsetsController.controlWindowInsetsAnimation(
                 systemBars(), 0, new LinearInterpolator(), cancellationSignal, listener);
-        verify(listener).onCancelled();
+        verify(listener).onCancelled(null);
         assertFalse(cancellationSignal.isCanceled());
     }
 
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index f254e4d..548af0c 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -114,7 +114,14 @@
                                         }
 
                                         @Override
-                                        public void onCancelled() {
+                                        public void onFinished(
+                                                WindowInsetsAnimationController controller) {
+                                            mAnimationController = null;
+                                        }
+
+                                        @Override
+                                        public void onCancelled(
+                                                WindowInsetsAnimationController controller) {
                                             mAnimationController = null;
                                         }
                                     });
@@ -230,7 +237,13 @@
                                         }
 
                                         @Override
-                                        public void onCancelled() {
+                                        public void onFinished(
+                                                WindowInsetsAnimationController controller) {
+                                        }
+
+                                        @Override
+                                        public void onCancelled(
+                                                WindowInsetsAnimationController controller) {
                                         }
                                     });
                         }