Merge "API update for insets"
diff --git a/api/current.txt b/api/current.txt
index d8de383..7e7cd9d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -51082,6 +51082,9 @@
     method public boolean dispatchUnhandledMove(android.view.View, int);
     method protected void dispatchVisibilityChanged(@NonNull android.view.View, int);
     method public void dispatchWindowFocusChanged(boolean);
+    method public void dispatchWindowInsetsAnimationFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+    method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets);
+    method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
     method public void dispatchWindowSystemUiVisiblityChanged(int);
     method public void dispatchWindowVisibilityChanged(int);
     method @CallSuper public void draw(android.graphics.Canvas);
@@ -51262,6 +51265,7 @@
     method @android.view.ViewDebug.ExportedProperty(category="layout") public final int getWidth();
     method protected int getWindowAttachCount();
     method public android.view.WindowId getWindowId();
+    method @Nullable public android.view.WindowInsetsController getWindowInsetsController();
     method public int getWindowSystemUiVisibility();
     method public android.os.IBinder getWindowToken();
     method public int getWindowVisibility();
@@ -51600,6 +51604,7 @@
     method public void setVisibility(int);
     method @Deprecated public void setWillNotCacheDrawing(boolean);
     method public void setWillNotDraw(boolean);
+    method public void setWindowInsetsAnimationCallback(@Nullable android.view.WindowInsetsAnimationCallback);
     method public void setX(float);
     method public void setY(float);
     method public void setZ(float);
@@ -52486,6 +52491,7 @@
     method public android.transition.Transition getExitTransition();
     method protected final int getFeatures();
     method protected final int getForcedWindowFlags();
+    method @Nullable public android.view.WindowInsetsController getInsetsController();
     method @NonNull public abstract android.view.LayoutInflater getLayoutInflater();
     method protected final int getLocalFeatures();
     method public android.media.session.MediaController getMediaController();
@@ -52701,7 +52707,9 @@
     method @NonNull public android.view.WindowInsets consumeStableInsets();
     method @NonNull public android.view.WindowInsets consumeSystemWindowInsets();
     method @Nullable public android.view.DisplayCutout getDisplayCutout();
+    method @NonNull public android.graphics.Insets getInsets(int);
     method @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
+    method @NonNull public android.graphics.Insets getMaxInsets(int) throws java.lang.IllegalArgumentException;
     method public int getStableInsetBottom();
     method public int getStableInsetLeft();
     method public int getStableInsetRight();
@@ -52720,6 +52728,7 @@
     method @NonNull public android.view.WindowInsets inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
     method public boolean isConsumed();
     method public boolean isRound();
+    method public boolean isVisible(int);
     method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int);
     method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(android.graphics.Rect);
   }
@@ -52729,11 +52738,76 @@
     ctor public WindowInsets.Builder(@NonNull android.view.WindowInsets);
     method @NonNull public android.view.WindowInsets build();
     method @NonNull public android.view.WindowInsets.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
+    method @NonNull public android.view.WindowInsets.Builder setInsets(int, @NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
+    method @NonNull public android.view.WindowInsets.Builder setMaxInsets(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
     method @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setTappableElementInsets(@NonNull android.graphics.Insets);
+    method @NonNull public android.view.WindowInsets.Builder setVisible(int, boolean);
+  }
+
+  public static final class WindowInsets.Type {
+    method public static int all();
+    method public static int captionBar();
+    method public static int ime();
+    method public static int mandatorySystemGestures();
+    method public static int navigationBars();
+    method public static int statusBars();
+    method public static int systemBars();
+    method public static int systemGestures();
+    method public static int tappableElement();
+    method public static int windowDecor();
+  }
+
+  public interface WindowInsetsAnimationCallback {
+    method public default void onFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+    method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets);
+    method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
+  }
+
+  public static final class WindowInsetsAnimationCallback.AnimationBounds {
+    ctor public WindowInsetsAnimationCallback.AnimationBounds(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets);
+    method @NonNull public android.graphics.Insets getLowerBound();
+    method @NonNull public android.graphics.Insets getUpperBound();
+    method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds inset(@NonNull android.graphics.Insets);
+  }
+
+  public static final class WindowInsetsAnimationCallback.InsetsAnimation {
+    ctor public WindowInsetsAnimationCallback.InsetsAnimation(int, @Nullable android.view.animation.Interpolator, long);
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method @Nullable public android.view.animation.Interpolator getInterpolator();
+    method public int getTypeMask();
+    method public void setDuration(long);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public interface WindowInsetsAnimationControlListener {
+    method public void onCancelled();
+    method public void onReady(@NonNull android.view.WindowInsetsAnimationController, int);
+  }
+
+  public interface WindowInsetsAnimationController {
+    method public void finish(boolean);
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method @NonNull public android.graphics.Insets getCurrentInsets();
+    method @NonNull public android.graphics.Insets getHiddenStateInsets();
+    method @NonNull public android.graphics.Insets getShownStateInsets();
+    method public int getTypes();
+    method public void setInsetsAndAlpha(@Nullable android.graphics.Insets, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public interface WindowInsetsController {
+    method public default void controlInputMethodAnimation(long, @NonNull android.view.WindowInsetsAnimationControlListener);
+    method public default void hideInputMethod();
+    method public void setSystemBarsAppearance(int);
+    method public void setSystemBarsBehavior(int);
+    method public default void showInputMethod();
+    field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10
+    field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8
   }
 
   public interface WindowManager extends android.view.ViewManager {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 28eb79a..71ac578 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,13 +16,13 @@
 
 package android.view;
 
-import static android.view.DisplayEventReceiver.CONFIG_CHANGED_EVENT_SUPPRESS;
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
 
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.FrameInfo;
+import android.graphics.Insets;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Build;
 import android.os.Handler;
@@ -219,9 +219,10 @@
     /**
      * Callback type: Animation callback to handle inset updates. This is separate from
      * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
-     * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
-     * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
-     * that contains all the combined updated insets.
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} for multiple
+     * ongoing animations but then update the whole view system with a single callback to
+     * {@link View#dispatchWindowInsetsAnimationProgress} that contains all the combined updated
+     * insets.
      * <p>
      * Both input and animation may change insets, so we need to run this after these callbacks, but
      * before traversals.
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 3d139cd..7ea4f30 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -21,7 +21,6 @@
 import static android.view.InsetsState.ISIDE_LEFT;
 import static android.view.InsetsState.ISIDE_RIGHT;
 import static android.view.InsetsState.ISIDE_TOP;
-import static android.view.InsetsState.toPublicType;
 
 import android.annotation.Nullable;
 import android.graphics.Insets;
@@ -34,7 +33,8 @@
 import android.view.InsetsState.InternalInsetsSide;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -66,20 +66,21 @@
     private final @InsetsType int mTypes;
     private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
     private final InsetsController mController;
-    private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
+    private final WindowInsetsAnimationCallback.InsetsAnimation mAnimation;
     private final Rect mFrame;
     private Insets mCurrentInsets;
     private Insets mPendingInsets;
+    private float mPendingFraction;
     private boolean mFinished;
     private boolean mCancelled;
-    private int mFinishedShownTypes;
+    private boolean mShownOnFinish;
 
     @VisibleForTesting
     public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
             InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetsType int types,
             Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier,
-            InsetsController controller) {
+            InsetsController controller, long durationMs) {
         mConsumers = consumers;
         mListener = listener;
         mTypes = types;
@@ -97,9 +98,10 @@
         // TODO: Check for controllability first and wait for IME if needed.
         listener.onReady(this, types);
 
-        mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets,
-                mShownInsets);
-        mController.dispatchAnimationStarted(mAnimation);
+        mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes,
+                InsetsController.INTERPOLATOR, durationMs);
+        mController.dispatchAnimationStarted(mAnimation,
+                new AnimationBounds(mHiddenInsets, mShownInsets));
     }
 
     @Override
@@ -123,7 +125,7 @@
     }
 
     @Override
-    public void changeInsets(Insets insets) {
+    public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
         if (mFinished) {
             throw new IllegalStateException(
                     "Can't change insets on an animation that is finished.");
@@ -132,6 +134,7 @@
             throw new IllegalStateException(
                     "Can't change insets on an animation that is cancelled.");
         }
+        mPendingFraction = sanitize(fraction);
         mPendingInsets = sanitize(insets);
         mController.scheduleApplyChangeInsets();
     }
@@ -155,30 +158,35 @@
         SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
         applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
         mCurrentInsets = mPendingInsets;
+        mAnimation.setFraction(mPendingFraction);
         if (mFinished) {
-            mController.notifyFinished(this, mFinishedShownTypes);
+            mController.notifyFinished(this, mShownOnFinish);
         }
         return mFinished;
     }
 
     @Override
-    public void finish(int shownTypes) {
+    public void finish(boolean shown) {
         if (mCancelled) {
             return;
         }
         InsetsState state = new InsetsState(mController.getState());
         for (int i = mConsumers.size() - 1; i >= 0; i--) {
             InsetsSourceConsumer consumer = mConsumers.valueAt(i);
-            boolean visible = (shownTypes & toPublicType(consumer.getType())) != 0;
-            state.getSource(consumer.getType()).setVisible(visible);
+            state.getSource(consumer.getType()).setVisible(shown);
         }
         Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */);
-        changeInsets(insets);
+        setInsetsAndAlpha(insets, 1f /* alpha */, shown ? 1f : 0f /* fraction */);
         mFinished = true;
-        mFinishedShownTypes = shownTypes;
+        mShownOnFinish = shown;
     }
 
+    @Override
     @VisibleForTesting
+    public float getCurrentFraction() {
+        return mAnimation.getFraction();
+    }
+
     public void onCancelled() {
         if (mFinished) {
             return;
@@ -191,6 +199,10 @@
         return mAnimation;
     }
 
+    WindowInsetsAnimationControlListener getListener() {
+        return mListener;
+    }
+
     private Insets calculateInsets(InsetsState state, Rect frame,
             SparseArray<InsetsSourceConsumer> consumers, boolean shown,
             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
@@ -210,9 +222,16 @@
     }
 
     private Insets sanitize(Insets insets) {
+        if (insets == null) {
+            insets = getCurrentInsets();
+        }
         return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
     }
 
+    private static float sanitize(float alpha) {
+        return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha);
+    }
+
     private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset,
             ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
         ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 43fec82..8870311 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -39,6 +39,8 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
@@ -55,11 +57,12 @@
 
     private static final int ANIMATION_DURATION_SHOW_MS = 275;
     private static final int ANIMATION_DURATION_HIDE_MS = 340;
-    private static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
     private static final int DIRECTION_NONE = 0;
     private static final int DIRECTION_SHOW = 1;
     private static final int DIRECTION_HIDE = 2;
 
+    static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
     @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
     private @interface AnimationDirection{}
 
@@ -85,8 +88,75 @@
             return object.getCurrentInsets();
         }
         @Override
-        public void set(WindowInsetsAnimationController object, Insets value) {
-            object.changeInsets(value);
+        public void set(WindowInsetsAnimationController controller, Insets value) {
+            controller.setInsetsAndAlpha(
+                    value, 1f /* alpha */, (((DefaultAnimationControlListener)
+                            ((InsetsAnimationControlImpl) controller).getListener())
+                                    .getRawProgress()));
+        }
+    }
+
+    private class DefaultAnimationControlListener implements WindowInsetsAnimationControlListener {
+
+        private WindowInsetsAnimationController mController;
+        private ObjectAnimator mAnimator;
+        private boolean mShow;
+
+        DefaultAnimationControlListener(boolean show) {
+            mShow = show;
+        }
+
+        @Override
+        public void onReady(WindowInsetsAnimationController controller, int types) {
+            mController = controller;
+            if (mShow) {
+                showDirectly(types);
+            } else {
+                hideDirectly(types);
+            }
+            mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE;
+            mAnimator = ObjectAnimator.ofObject(
+                    controller,
+                    new InsetsProperty(),
+                    sEvaluator,
+                    mShow ? controller.getHiddenStateInsets() : controller.getShownStateInsets(),
+                    mShow ? controller.getShownStateInsets() : controller.getHiddenStateInsets()
+            );
+            mAnimator.setDuration(getDurationMs());
+            mAnimator.setInterpolator(INTERPOLATOR);
+            mAnimator.addListener(new AnimatorListenerAdapter() {
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    onAnimationFinish();
+                }
+            });
+            mAnimator.start();
+        }
+
+        @Override
+        public void onCancelled() {
+            // Animator can be null when it is cancelled before onReady() completes.
+            if (mAnimator != null) {
+                mAnimator.cancel();
+            }
+        }
+
+        private void onAnimationFinish() {
+            mAnimationDirection = DIRECTION_NONE;
+            mController.finish(mShow);
+        }
+
+        private float getRawProgress() {
+            float fraction = (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration();
+            return mShow ? fraction : 1 - fraction;
+        }
+
+        private long getDurationMs() {
+            if (mAnimator != null) {
+                return mAnimator.getDuration();
+            }
+            return mShow ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS;
         }
     }
 
@@ -278,24 +348,25 @@
     }
 
     @Override
-    public void controlWindowInsetsAnimation(@InsetsType int types,
+    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
             WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(types, listener, false /* fromIme */);
+        controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs);
     }
 
     private void controlWindowInsetsAnimation(@InsetsType int types,
-            WindowInsetsAnimationControlListener listener, boolean fromIme) {
+            WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs) {
         // 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;
         }
-        controlAnimationUnchecked(types, listener, mFrame, fromIme);
+        controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs);
     }
 
     private void controlAnimationUnchecked(@InsetsType int types,
-            WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme) {
+            WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
+            long durationMs) {
         if (types == 0) {
             // nothing to animate.
             return;
@@ -326,7 +397,7 @@
 
         final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
                 frame, mState, listener, typesReady,
-                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
+                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this, durationMs);
         mAnimationControls.add(controller);
     }
 
@@ -397,10 +468,13 @@
     }
 
     @VisibleForTesting
-    public void notifyFinished(InsetsAnimationControlImpl controller, int shownTypes) {
+    public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
         mAnimationControls.remove(controller);
-        hideDirectly(controller.getTypes() & ~shownTypes);
-        showDirectly(controller.getTypes() & shownTypes);
+        if (shown) {
+            showDirectly(controller.getTypes());
+        } else {
+            hideDirectly(controller.getTypes());
+        }
     }
 
     void notifyControlRevoked(InsetsSourceConsumer consumer) {
@@ -510,58 +584,11 @@
             return;
         }
 
-        WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
-
-            private WindowInsetsAnimationController mController;
-            private ObjectAnimator mAnimator;
-
-            @Override
-            public void onReady(WindowInsetsAnimationController controller, int types) {
-                mController = controller;
-                if (show) {
-                    showDirectly(types);
-                } else {
-                    hideDirectly(types);
-                }
-                mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
-                mAnimator = ObjectAnimator.ofObject(
-                        controller,
-                        new InsetsProperty(),
-                        sEvaluator,
-                        show ? controller.getHiddenStateInsets() : controller.getShownStateInsets(),
-                        show ? controller.getShownStateInsets() : controller.getHiddenStateInsets()
-                );
-                mAnimator.setDuration(show
-                        ? ANIMATION_DURATION_SHOW_MS
-                        : ANIMATION_DURATION_HIDE_MS);
-                mAnimator.setInterpolator(INTERPOLATOR);
-                mAnimator.addListener(new AnimatorListenerAdapter() {
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        onAnimationFinish();
-                    }
-                });
-                mAnimator.start();
-            }
-
-            @Override
-            public void onCancelled() {
-                // Animator can be null when it is cancelled before onReady() completes.
-                if (mAnimator != null) {
-                    mAnimator.cancel();
-                }
-            }
-
-            private void onAnimationFinish() {
-                mAnimationDirection = DIRECTION_NONE;
-                mController.finish(show ? types : 0);
-            }
-        };
-
+        final DefaultAnimationControlListener listener = new DefaultAnimationControlListener(show);
         // Show/hide animations always need to be relative to the display frame, in order that shown
         // and hidden state insets are correct.
-        controlAnimationUnchecked(types, listener, mState.getDisplayFrame(), fromIme);
+        controlAnimationUnchecked(
+                types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs());
     }
 
     private void hideDirectly(@InsetsType int types) {
@@ -592,12 +619,12 @@
     }
 
     @VisibleForTesting
-    public void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
-        mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
+    public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) {
+        mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds);
     }
 
     @VisibleForTesting
-    public void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
+    public void dispatchAnimationFinished(InsetsAnimation animation) {
         mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9d4f3878..2d0b954 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -109,7 +109,8 @@
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -4626,7 +4627,7 @@
 
         private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
 
-        private WindowInsetsAnimationListener mWindowInsetsAnimationListener;
+        private WindowInsetsAnimationCallback mWindowInsetsAnimationCallback;
 
         /**
          * This lives here since it's only valid for interactive views.
@@ -11091,33 +11092,55 @@
     }
 
     /**
-     * Sets a {@link WindowInsetsAnimationListener} to be notified about animations of windows that
+     * Sets a {@link WindowInsetsAnimationCallback} to be notified about animations of windows that
      * cause insets.
      *
      * @param listener The listener to set.
-     * @hide pending unhide
      */
-    public void setWindowInsetsAnimationListener(WindowInsetsAnimationListener listener) {
-        getListenerInfo().mWindowInsetsAnimationListener = listener;
+    public void setWindowInsetsAnimationCallback(@Nullable WindowInsetsAnimationCallback listener) {
+        getListenerInfo().mWindowInsetsAnimationCallback = listener;
     }
 
-    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
-        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
-            mListenerInfo.mWindowInsetsAnimationListener.onStarted(animation);
+    /**
+     * Dispatches {@link WindowInsetsAnimationCallback#onStarted(InsetsAnimation, AnimationBounds)}
+     * when Window Insets animation is started.
+     * @param animation current animation
+     * @param bounds the upper and lower {@link AnimationBounds} that provides range of
+     *  {@link InsetsAnimation}.
+     * @return the upper and lower {@link AnimationBounds}.
+     */
+    @NonNull
+    public AnimationBounds dispatchWindowInsetsAnimationStarted(
+            @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+            return mListenerInfo.mWindowInsetsAnimationCallback.onStarted(animation, bounds);
         }
+        return bounds;
     }
 
-    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
-        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
-            return mListenerInfo.mWindowInsetsAnimationListener.onProgress(insets);
+    /**
+     * Dispatches {@link WindowInsetsAnimationCallback#onProgress(WindowInsets)}
+     * when Window Insets animation makes progress.
+     * @param insets The current {@link WindowInsets}.
+     * @return current {@link WindowInsets}.
+     */
+    @NonNull
+    public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+            return mListenerInfo.mWindowInsetsAnimationCallback.onProgress(insets);
         } else {
             return insets;
         }
     }
 
-    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
-        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
-            mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
+    /**
+     * Dispatches {@link WindowInsetsAnimationCallback#onFinished(InsetsAnimation)}
+     * when Window Insets animation finishes.
+     * @param animation The current ongoing {@link InsetsAnimation}.
+     */
+    public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+            mListenerInfo.mWindowInsetsAnimationCallback.onFinished(animation);
         }
     }
 
@@ -11253,7 +11276,6 @@
      * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
      *         a window.
      * @see Window#getInsetsController()
-     * @hide pending unhide
      */
     public @Nullable WindowInsetsController getWindowInsetsController() {
         if (mAttachInfo != null) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 853a302..4334bb5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -51,7 +51,8 @@
 import android.util.Pools.SynchronizedPool;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -7198,16 +7199,20 @@
     }
 
     @Override
-    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
-        super.dispatchWindowInsetsAnimationStarted(animation);
+    @NonNull
+    public AnimationBounds dispatchWindowInsetsAnimationStarted(
+            @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
+        super.dispatchWindowInsetsAnimationStarted(animation, bounds);
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
-            getChildAt(i).dispatchWindowInsetsAnimationStarted(animation);
+            getChildAt(i).dispatchWindowInsetsAnimationStarted(animation, bounds);
         }
+        return bounds;
     }
 
     @Override
-    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
+    @NonNull
+    public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets) {
         insets = super.dispatchWindowInsetsAnimationProgress(insets);
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
@@ -7217,7 +7222,7 @@
     }
 
     @Override
-    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
+    public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
         super.dispatchWindowInsetsAnimationFinished(animation);
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index af1882b..ff31115 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2573,7 +2573,8 @@
     /**
      * @return The {@link WindowInsetsController} associated with this window
      * @see View#getWindowInsetsController()
-     * @hide pending unhide
      */
-    public abstract @NonNull WindowInsetsController getInsetsController();
+    public @Nullable WindowInsetsController getInsetsController() {
+        return null;
+    }
 }
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index b16a4ca..2404c84 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -177,7 +177,7 @@
      * @return The insets that include system bars indicated by {@code typeMask}, taken from
      *         {@code typeInsetsMap}.
      */
-    private static Insets getInsets(Insets[] typeInsetsMap, @InsetsType int typeMask) {
+    static Insets getInsets(Insets[] typeInsetsMap, @InsetsType int typeMask) {
         Insets result = null;
         for (int i = FIRST; i <= LAST; i = i << 1) {
             if ((typeMask & i) == 0) {
@@ -289,9 +289,8 @@
      *
      * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
      * @return The insets.
-     *
-     * @hide pending unhide
      */
+    @NonNull
     public Insets getInsets(@InsetsType int typeMask) {
         return getInsets(mTypeInsetsMap, typeMask);
     }
@@ -313,8 +312,8 @@
      *                                  insets are not available for this type as the height of the
      *                                  IME is dynamic depending on the {@link EditorInfo} of the
      *                                  currently focused view, as well as the UI state of the IME.
-     * @hide pending unhide
      */
+    @NonNull
     public Insets getMaxInsets(@InsetsType int typeMask) throws IllegalArgumentException {
         if ((typeMask & IME) != 0) {
             throw new IllegalArgumentException("Unable to query the maximum insets for IME");
@@ -329,7 +328,6 @@
      * @param typeMask Bit mask of {@link Type.InsetsType}s to query visibility status.
      * @return {@code true} if and only if all windows included in {@code typeMask} are currently
      *         visible on screen.
-     * @hide pending unhide
      */
     public boolean isVisible(@InsetsType int typeMask) {
         for (int i = FIRST; i <= LAST; i = i << 1) {
@@ -874,7 +872,7 @@
         return typeInsetsMap;
     }
 
-    private static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
+    static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
         int newLeft = Math.max(0, insets.left - left);
         int newTop = Math.max(0, insets.top - top);
         int newRight = Math.max(0, insets.right - right);
@@ -1015,7 +1013,6 @@
          * @param insets The insets to set.
          *
          * @return itself
-         * @hide pending unhide
          */
         @NonNull
         public Builder setInsets(@InsetsType int typeMask, @NonNull Insets insets) {
@@ -1046,7 +1043,6 @@
          *                                  the IME is dynamic depending on the {@link EditorInfo}
          *                                  of the currently focused view, as well as the UI
          *                                  state of the IME.
-         * @hide pending unhide
          */
         @NonNull
         public Builder setMaxInsets(@InsetsType int typeMask, @NonNull Insets insets)
@@ -1070,7 +1066,6 @@
          * @param visible Whether to mark the windows as visible or not.
          *
          * @return itself
-         * @hide pending unhide
          */
         @NonNull
         public Builder setVisible(@InsetsType int typeMask, boolean visible) {
@@ -1145,7 +1140,6 @@
 
     /**
      * Class that defines different types of sources causing window insets.
-     * @hide pending unhide
      */
     public static final class Type {
 
diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java
new file mode 100644
index 0000000..8ae8520
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationCallback.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Insets;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.animation.Interpolator;
+
+/**
+ * Interface that allows the application to listen to animation events for windows that cause
+ * insets.
+ */
+public interface WindowInsetsAnimationCallback {
+
+    /**
+     * Called when an inset animation gets started.
+     * <p>
+     * Note that, like {@link #onProgress}, dispatch of the animation start event is hierarchical:
+     * It will starts at the root of the view hierarchy and then traverse it and invoke the callback
+     * of the specific {@link View} that is being traversed. The method my return a modified
+     * instance of the bounds by calling {@link AnimationBounds#inset} to indicate that a part of
+     * the insets have been used to offset or clip its children, and the children shouldn't worry
+     * about that part anymore.
+     *
+     * @param animation The animation that is about to start.
+     * @param bounds The bounds in which animation happens.
+     * @return The animation representing the part of the insets that should be dispatched to the
+     *         subtree of the hierarchy.
+     */
+    @NonNull
+    default AnimationBounds onStarted(
+            @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
+        return bounds;
+    }
+
+    /**
+     * Called when the insets change as part of running an animation. Note that even if multiple
+     * animations for different types are running, there will only be one progress callback per
+     * frame. The {@code insets} passed as an argument represents the overall state and will include
+     * all types, regardless of whether they are animating or not.
+     * <p>
+     * Note that insets dispatch is hierarchical: It will start at the root of the view hierarchy,
+     * and then traverse it and invoke the callback of the specific {@link View} being traversed.
+     * The method may return a modified instance by calling
+     * {@link WindowInsets#inset(int, int, int, int)} to indicate that a part of the insets have
+     * been used to offset or clip its children, and the children shouldn't worry about that part
+     * anymore.
+     * TODO: Introduce a way to map (type -> InsetAnimation) so app developer can query animation
+     *  for a given type e.g. callback.getAnimation(type) OR controller.getAnimation(type).
+     *  Or on the controller directly?
+     * @param insets The current insets.
+     * @return The insets to dispatch to the subtree of the hierarchy.
+     */
+    @NonNull
+    WindowInsets onProgress(@NonNull WindowInsets insets);
+
+    /**
+     * Called when an inset animation has finished.
+     *
+     * @param animation The animation that has finished running. This will be the same instance as
+     *                  passed into {@link #onStarted}
+     */
+    default void onFinished(@NonNull InsetsAnimation animation) {
+    }
+
+    /**
+     * Class representing an animation of a set of windows that cause insets.
+     */
+    final class InsetsAnimation {
+
+        private final @InsetsType int mTypeMask;
+        private float mFraction;
+        @Nullable private final Interpolator mInterpolator;
+        private long mDurationMs;
+
+        public InsetsAnimation(
+                @InsetsType int typeMask, @Nullable Interpolator interpolator, long durationMs) {
+            mTypeMask = typeMask;
+            mInterpolator = interpolator;
+            mDurationMs = durationMs;
+        }
+
+        /**
+         * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating.
+         */
+        public @InsetsType int getTypeMask() {
+            return mTypeMask;
+        }
+
+        /**
+         * Returns the raw fractional progress of this animation between
+         * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note
+         * that this progress is the global progress of the animation, whereas
+         * {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may
+         * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
+         * Progress per insets animation is global for the entire animation. One animation animates
+         * all things together (in, out, ...). If they don't animate together, we'd have
+         * multiple animations.
+         *
+         * @return The current progress of this animation.
+         */
+        @FloatRange(from = 0f, to = 1f)
+        public float getFraction() {
+            return mFraction;
+        }
+
+        /**
+         * Returns the interpolated fractional progress of this animation between
+         * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note
+         * that this progress is the global progress of the animation, whereas
+         * {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may
+         * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
+         * Progress per insets animation is global for the entire animation. One animation animates
+         * all things together (in, out, ...). If they don't animate together, we'd have
+         * multiple animations.
+         * @see #getFraction() for raw fraction.
+         * @return The current interpolated progress of this animation. -1 if interpolator isn't
+         * specified.
+         */
+        public float getInterpolatedFraction() {
+            if (mInterpolator != null) {
+                return mInterpolator.getInterpolation(mFraction);
+            }
+            return -1;
+        }
+
+        @Nullable
+        public Interpolator getInterpolator() {
+            return mInterpolator;
+        }
+
+        /**
+         * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}.
+         */
+        public long getDurationMillis() {
+            return mDurationMs;
+        }
+
+        /**
+         * Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is
+         * controlled by the app {@see #getCurrentFraction}.
+         * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set progress either.
+         * Progress would be set by system with the system-default animation.
+         * </p>
+         * @param fraction fractional progress between 0 and 1 where 0 represents hidden and
+         *                zero progress and 1 represent fully shown final state.
+         */
+        public void setFraction(@FloatRange(from = 0f, to = 1f) float fraction) {
+            mFraction = fraction;
+        }
+
+        /**
+         * Set duration of the animation if {@link WindowInsets.Type.InsetsType} animation is
+         * controlled by the app.
+         * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set duration either.
+         * Duration would be set by system with the system-default animation.
+         * </p>
+         * @param durationMs in {@link java.util.concurrent.TimeUnit#MILLISECONDS}
+         */
+        public void setDuration(long durationMs) {
+            mDurationMs = durationMs;
+        }
+    }
+
+    /**
+     * Class representing the range of an {@link InsetsAnimation}
+     */
+    final class AnimationBounds {
+        private final Insets mLowerBound;
+        private final Insets mUpperBound;
+
+        public AnimationBounds(@NonNull Insets lowerBound, @NonNull Insets upperBound) {
+            mLowerBound = lowerBound;
+            mUpperBound = upperBound;
+        }
+
+        /**
+         * Queries the lower inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
+         * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This
+         * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
+         * invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationController}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * </p>
+         * There are no overlapping animations for a specific type, but there may be multiple
+         * animations running at the same time for different inset types.
+         *
+         * @see #getUpperBound()
+         * @see WindowInsetsAnimationController#getHiddenStateInsets
+         */
+        @NonNull
+        public Insets getLowerBound() {
+            return mLowerBound;
+        }
+
+        /**
+         * Queries the upper inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE}
+         * nd the upper bound is the same as {@link WindowInsets#getInsets(int)} for the fully
+         * shown state. This is the same as
+         * {@link WindowInsetsAnimationController#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
+         * invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationController}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * <p>
+         * There are no overlapping animations for a specific type, but there may be multiple
+         * animations running at the same time for different inset types.
+         *
+         * @see #getLowerBound()
+         * @see WindowInsetsAnimationController#getShownStateInsets
+         */
+        @NonNull
+        public Insets getUpperBound() {
+            return mUpperBound;
+        }
+
+        /**
+         * Insets both the lower and upper bound by the specified insets. This is to be used in
+         * {@link WindowInsetsAnimationCallback#onStarted} to indicate that a part of the insets has
+         * been used to offset or clip its children, and the children shouldn't worry about that
+         * part anymore.
+         *
+         * @param insets The amount to inset.
+         * @return A copy of this instance inset in the given directions.
+         * @see WindowInsets#inset
+         * @see WindowInsetsAnimationCallback#onStarted
+         */
+        @NonNull
+        public AnimationBounds inset(@NonNull Insets insets) {
+            return new AnimationBounds(
+                    // TODO: refactor so that WindowInsets.insetInsets() is in a more appropriate
+                    //  place eventually.
+                    WindowInsets.insetInsets(
+                            mLowerBound, insets.left, insets.top, insets.right, insets.bottom),
+                    WindowInsets.insetInsets(
+                            mUpperBound, insets.left, insets.top, insets.right, insets.bottom));
+        }
+    }
+}
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index 33fb327..8a226c1 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -22,19 +22,17 @@
 
 /**
  * Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
- * @hide pending unhide
  */
 public interface WindowInsetsAnimationControlListener {
 
     /**
-     * Gets called as soon as 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.
+     * 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.
      *
      * @param controller The controller to control the inset animation.
      * @param types The {@link InsetsType}s it was able to gain control over. Note that this may be
      *              different than the types passed into
-     *              {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
+     *              {@link WindowInsetsController#controlInputMethodAnimation} 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.
      */
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 5cbf3b8..5149103 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -16,83 +16,127 @@
 
 package android.view;
 
+import android.annotation.FloatRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.graphics.Insets;
 import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
 
 /**
- * Interface to control a window inset animation frame-by-frame.
- * @hide pending unhide
+ * Controller for app-driven animation of system windows.
+ *  <p>
+ *  {@code WindowInsetsAnimationController} lets apps animate system windows such as
+ *  the {@link android.inputmethodservice.InputMethodService IME}. The animation is
+ *  synchronized, such that changes the system windows and the app's current frame
+ *  are rendered at the same time.
+ *  <p>
+ *  Control is obtained through {@link WindowInsetsController#controlInputMethodAnimation}.
  */
+@SuppressLint("NotClosable")
 public interface WindowInsetsAnimationController {
 
     /**
      * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
      * <p>
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
+     * <p>
      * If there are any animation listeners registered, this value is the same as
-     * {@link InsetsAnimation#getLowerBound()} that will be passed into the callbacks.
+     * {@link AnimationBounds#getLowerBound()} that is being be passed into the root view of the
+     * hierarchy.
      *
      * @return Insets when the windows this animation is controlling are fully hidden.
      *
-     * @see InsetsAnimation#getLowerBound()
+     * @see AnimationBounds#getLowerBound()
      */
     @NonNull Insets getHiddenStateInsets();
 
     /**
      * Retrieves the {@link Insets} when the windows this animation is controlling are fully shown.
      * <p>
-     * In case the size of a window causing insets is changing in the middle of the animation, we
-     * execute that height change after this animation has finished.
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
      * <p>
      * If there are any animation listeners registered, this value is the same as
-     * {@link InsetsAnimation#getUpperBound()} that will be passed into the callbacks.
+     * {@link AnimationBounds#getUpperBound()} that is being passed into the root view of hierarchy.
      *
      * @return Insets when the windows this animation is controlling are fully shown.
      *
-     * @see InsetsAnimation#getUpperBound()
+     * @see AnimationBounds#getUpperBound()
      */
     @NonNull Insets getShownStateInsets();
 
     /**
-     * @return The current insets on the window. These will follow any animation changes.
+     * Retrieves the current insets.
+     * <p>
+     * Note that these insets are always relative to the window, which is the same as
+     * being relative
+     * to {@link View#getRootView}
+     * @return The current insets on the currently showing frame. These insets will change as the
+     * animation progresses to reflect the current insets provided by the controlled window.
      */
     @NonNull Insets getCurrentInsets();
 
     /**
+     *  Returns the progress as previously set by {@code fraction} in {@link #setInsetsAndAlpha}
+     *
+     *  @return the progress of the animation, where {@code 0} is fully hidden and {@code 1} is
+     *  fully shown.
+     * <p>
+     *  Note: this value represents raw overall progress of the animation
+     *  i.e. the combined progress of insets and alpha.
+     *  <p>
+     */
+    @FloatRange(from = 0f, to = 1f)
+    float getCurrentFraction();
+
+    /**
      * @return The {@link InsetsType}s this object is currently controlling.
      */
     @InsetsType int getTypes();
 
     /**
-     * Modifies the insets by indirectly moving the windows around in the system that are causing
-     * window insets.
+     * Modifies the insets for the frame being drawn by indirectly moving the windows around in the
+     * system that are causing window insets.
      * <p>
-     * Note that this will <b>not</b> inform the view system of a full inset change via
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
+     * <p>
+     * Also note that this will <b>not</b> inform the view system of a full inset change via
      * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
      * animation. If you'd like to animate views during a window inset animation, register a
-     * {@link WindowInsetsAnimationListener} by calling
-     * {@link View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)} that will be
-     * notified about any insets change via {@link WindowInsetsAnimationListener#onProgress} during
+     * {@link WindowInsetsAnimationCallback} by calling
+     * {@link View#setWindowInsetsAnimationCallback(WindowInsetsAnimationCallback)} that will be
+     * notified about any insets change via {@link WindowInsetsAnimationCallback#onProgress} during
      * the animation.
      * <p>
      * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
      * finished, i.e. once {@link #finish} has been called.
+     * Note: If there are no insets, alpha animation is still applied.
      *
      * @param insets The new insets to apply. Based on the requested insets, the system will
      *               calculate the positions of the windows in the system causing insets such that
      *               the resulting insets of that configuration will match the passed in parameter.
      *               Note that these insets are being clamped to the range from
-     *               {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
+     *               {@link #getHiddenStateInsets} to {@link #getShownStateInsets}.
+     *               If you intend on changing alpha only, pass null or {@link #getCurrentInsets()}.
+     * @param alpha  The new alpha to apply to the inset side.
+     * @param fraction instantaneous animation progress. This value is dispatched to
+     *                 {@link WindowInsetsAnimationCallback}.
      *
-     * @see WindowInsetsAnimationListener
-     * @see View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)
+     * @see WindowInsetsAnimationCallback
+     * @see View#setWindowInsetsAnimationCallback(WindowInsetsAnimationCallback)
      */
-    void changeInsets(@NonNull Insets insets);
+    void setInsetsAndAlpha(@Nullable Insets insets, @FloatRange(from = 0f, to = 1f) float alpha,
+            @FloatRange(from = 0f, to = 1f) float fraction);
 
     /**
-     * @param shownTypes The list of windows causing insets that should remain shown after finishing
-     *                   the animation.
+     * Finishes the animation, and leaves the windows shown or hidden. After invoking
+     * {@link #finish(boolean)}, this instance is no longer valid.
+     * @param shown if {@code true}, the windows will be shown after finishing the
+     *              animation. Otherwise they will be hidden.
      */
-    void finish(@InsetsType int shownTypes);
+    void finish(boolean shown);
 }
diff --git a/core/java/android/view/WindowInsetsAnimationListener.java b/core/java/android/view/WindowInsetsAnimationListener.java
deleted file mode 100644
index f734b4b..0000000
--- a/core/java/android/view/WindowInsetsAnimationListener.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.graphics.Insets;
-
-/**
- * Interface that allows the application to listen to animation events for windows that cause
- * insets.
- * @hide pending unhide
- */
-public interface WindowInsetsAnimationListener {
-
-    /**
-     * Called when an inset animation gets started.
-     *
-     * @param animation The animation that is about to start.
-     */
-    void onStarted(InsetsAnimation animation);
-
-    /**
-     * Called when the insets change as part of running an animation. Note that even if multiple
-     * animations for different types are running, there will only be one progress callback per
-     * frame. The {@code insets} passed as an argument represents the overall state and will include
-     * all types, regardless of whether they are animating or not.
-     * <p>
-     * Note that insets dispatch is hierarchical: It will start at the root of the view hierarchy,
-     * and then traverse it and invoke the callback of the specific {@link View} being traversed.
-     * The callback may return a modified instance by calling {@link WindowInsets#inset(int, int, int, int)}
-     * to indicate that a part of the insets have been used to offset or clip its children, and the
-     * children shouldn't worry about that part anymore.
-     *
-     * @param insets The current insets.
-     * @return The insets to dispatch to the subtree of the hierarchy.
-     */
-    WindowInsets onProgress(WindowInsets insets);
-
-    /**
-     * Called when an inset animation has finished.
-     *
-     * @param animation The animation that has finished running.
-     */
-    void onFinished(InsetsAnimation animation);
-
-    /**
-     * Class representing an animation of a set of windows that cause insets.
-     */
-    class InsetsAnimation {
-
-        private final @WindowInsets.Type.InsetsType int mTypeMask;
-        private final Insets mLowerBound;
-        private final Insets mUpperBound;
-
-        /**
-         * @hide
-         */
-        InsetsAnimation(int typeMask, Insets lowerBound, Insets upperBound) {
-            mTypeMask = typeMask;
-            mLowerBound = lowerBound;
-            mUpperBound = upperBound;
-        }
-
-        /**
-         * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating.
-         */
-        public @WindowInsets.Type.InsetsType int getTypeMask() {
-            return mTypeMask;
-        }
-
-        /**
-         * Queries the lower inset bound of the animation. If the animation is about showing or
-         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
-         * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This
-         * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and
-         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
-         * invoked because of an animation that originates from
-         * {@link WindowInsetsAnimationController}.
-         * <p>
-         * However, if the size of a window that causes insets is changing, these are the
-         * lower/upper bounds of that size animation.
-         * <p>
-         * There are no overlapping animations for a specific type, but there may be two animations
-         * running at the same time for different inset types.
-         *
-         * @see #getUpperBound()
-         * @see WindowInsetsAnimationController#getHiddenStateInsets
-         * TODO: It's a bit weird that these are global per window but onProgress is hierarchical.
-         * TODO: If multiple types are animating, querying the bound per type isn't possible. Should
-         * we:
-         * 1. Offer bounds by type here?
-         * 2. Restrict one animation to one single type only?
-         * Returning WindowInsets here isn't feasible in case of overlapping animations: We can't
-         * fill in the insets for the types from the other animation into the WindowInsets object
-         * as it's changing as well.
-         */
-        public Insets getLowerBound() {
-            return mLowerBound;
-        }
-
-        /**
-         * @see #getLowerBound()
-         * @see WindowInsetsAnimationController#getShownStateInsets
-         */
-        public Insets getUpperBound() {
-            return mUpperBound;
-        }
-    }
-}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index a045a6a..6de56be 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -30,7 +30,6 @@
  * Interface to control windows that generate insets.
  *
  * TODO Needs more information and examples once the API is more baked.
- * @hide pending unhide
  */
 public interface WindowInsetsController {
 
@@ -64,7 +63,10 @@
      */
     int APPEARANCE_LIGHT_NAVIGATION_BARS = 1 << 4;
 
-    /** Determines the appearance of system bars. */
+    /**
+     * Determines the appearance of system bars.
+     * @hide
+     */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {APPEARANCE_OPAQUE_STATUS_BARS, APPEARANCE_OPAQUE_NAVIGATION_BARS,
             APPEARANCE_LOW_PROFILE_BARS, APPEARANCE_LIGHT_STATUS_BARS,
@@ -75,33 +77,40 @@
     /**
      * The default option for {@link #setSystemBarsBehavior(int)}. System bars will be forcibly
      * shown on any user interaction on the corresponding display if navigation bars are hidden by
-     * {@link #hide(int)} or {@link WindowInsetsAnimationController#changeInsets(Insets)}.
+     * {@link #hide(int)} or
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
+     * @hide
      */
     int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0;
 
     /**
      * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when
      * hiding navigation bars by calling {@link #hide(int)} or
-     * {@link WindowInsetsAnimationController#changeInsets(Insets)}.
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
      *
      * <p>When system bars are hidden in this mode, they can be revealed with system gestures, such
      * as swiping from the edge of the screen where the bar is hidden from.</p>
+     * @hide
      */
     int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1;
 
     /**
      * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when
      * hiding navigation bars by calling {@link #hide(int)} or
-     * {@link WindowInsetsAnimationController#changeInsets(Insets)}.
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
      *
      * <p>When system bars are hidden in this mode, they can be revealed temporarily with system
      * gestures, such as swiping from the edge of the screen where the bar is hidden from. These
      * transient system bars will overlay app’s content, may have some degree of transparency, and
      * will automatically hide after a short timeout.</p>
+     * @hide
      */
     int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2;
 
-    /** Determines the behavior of system bars when hiding them by calling {@link #hide}. */
+    /**
+     * Determines the behavior of system bars when hiding them by calling {@link #hide}.
+     * @hide
+     */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {BEHAVIOR_SHOW_BARS_BY_TOUCH, BEHAVIOR_SHOW_BARS_BY_SWIPE,
             BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE})
@@ -139,23 +148,27 @@
      * the position of the windows in the system causing insets directly.
      *
      * @param types The {@link InsetsType}s the application has requested to control.
+     * @param durationMillis duration of animation in
+     *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}
      * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
      *                 windows are ready to be controlled, among other callbacks.
      * @hide
      */
-    void controlWindowInsetsAnimation(@InsetsType int types,
+    void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
             @NonNull WindowInsetsAnimationControlListener listener);
 
     /**
      * Lets the application control the animation for showing the IME in a frame-by-frame manner by
      * modifying the position of the IME when it's causing insets.
      *
+     * @param durationMillis duration of the animation in
+     *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}
      * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
      *                 IME are ready to be controlled, among other callbacks.
      */
-    default void controlInputMethodAnimation(
+    default void controlInputMethodAnimation(long durationMillis,
             @NonNull WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(ime(), listener);
+        controlWindowInsetsAnimation(ime(), durationMillis, listener);
     }
 
     /**
@@ -166,7 +179,7 @@
      * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
      * {@link WindowInsets#isVisible}.
      *
-     * @see #controlInputMethodAnimation(WindowInsetsAnimationControlListener)
+     * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)
      * @see #hideInputMethod()
      */
     default void showInputMethod() {
@@ -181,7 +194,7 @@
      * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
      * {@link WindowInsets#isVisible}.
      *
-     * @see #controlInputMethodAnimation(WindowInsetsAnimationControlListener)
+     * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)
      * @see #showInputMethod()
      */
     default void hideInputMethod() {
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 1a48260..cce38f6 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -19,11 +19,9 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.systemBars;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
@@ -118,7 +116,7 @@
         consumers.put(ITYPE_NAVIGATION_BAR, navConsumer);
         mController = new InsetsAnimationControlImpl(consumers,
                 new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
-                () -> mMockTransactionApplier, mMockController);
+                () -> mMockTransactionApplier, mMockController, 10 /* durationMs */);
     }
 
     @Test
@@ -131,7 +129,8 @@
 
     @Test
     public void testChangeInsets() {
-        mController.changeInsets(Insets.of(0, 30, 40, 0));
+        mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 1f /* alpha */,
+                0f /* fraction */);
         mController.applyChangeInsets(new InsetsState());
         assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
 
@@ -150,24 +149,24 @@
     @Test
     public void testFinishing() {
         when(mMockController.getState()).thenReturn(mInsetsState);
-        mController.finish(navigationBars());
+        mController.finish(true /* shown */);
         mController.applyChangeInsets(mInsetsState);
-        assertFalse(mInsetsState.getSource(ITYPE_STATUS_BAR).isVisible());
+        assertTrue(mInsetsState.getSource(ITYPE_STATUS_BAR).isVisible());
         assertTrue(mInsetsState.getSource(ITYPE_NAVIGATION_BAR).isVisible());
-        assertEquals(Insets.of(0, 0, 100, 0), mController.getCurrentInsets());
-        verify(mMockController).notifyFinished(eq(mController), eq(navigationBars()));
+        assertEquals(Insets.of(0, 100, 100, 0), mController.getCurrentInsets());
+        verify(mMockController).notifyFinished(eq(mController), eq(true /* shown */));
     }
 
     @Test
     public void testCancelled() {
         mController.onCancelled();
         try {
-            mController.changeInsets(Insets.NONE);
+            mController.setInsetsAndAlpha(Insets.NONE, 1f /*alpha */, 0f /* fraction */);
             fail("Expected exception to be thrown");
         } catch (IllegalStateException ignored) {
         }
         verify(mMockListener).onCancelled();
-        mController.finish(navigationBars());
+        mController.finish(true /* shown */);
     }
 
     private void assertPosition(Matrix m, Rect original, Rect transformed) {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index e4d8279..a89fc1e 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -123,7 +123,7 @@
 
         WindowInsetsAnimationControlListener mockListener =
                 mock(WindowInsetsAnimationControlListener.class);
-        mController.controlWindowInsetsAnimation(statusBars(), mockListener);
+        mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */, mockListener);
         verify(mockListener).onReady(any(), anyInt());
         mController.onControlsChanged(new InsetsSourceControl[0]);
         verify(mockListener).onCancelled();
@@ -135,7 +135,7 @@
         mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
         WindowInsetsAnimationControlListener controlListener =
                 mock(WindowInsetsAnimationControlListener.class);
-        mController.controlWindowInsetsAnimation(0, controlListener);
+        mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, controlListener);
         verify(controlListener).onCancelled();
         verify(controlListener, never()).onReady(any(), anyInt());
     }
@@ -331,12 +331,13 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener mockListener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlWindowInsetsAnimation(statusBars(), mockListener);
+            mController.controlWindowInsetsAnimation(statusBars(), 0 /* durationMs */,
+                    mockListener);
 
             ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
                     ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
             verify(mockListener).onReady(controllerCaptor.capture(), anyInt());
-            controllerCaptor.getValue().finish(0 /* shownTypes */);
+            controllerCaptor.getValue().finish(false /* shown */);
         });
         waitUntilNextFrame();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {