WindowInsetsAnimationControllerCompat.java
/*
* Copyright 2020 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 androidx.core.view;
import android.os.Build;
import android.view.View;
import android.view.WindowInsetsAnimationController;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.graphics.Insets;
import androidx.core.view.WindowInsetsCompat.Type;
import androidx.core.view.WindowInsetsCompat.Type.InsetsType;
/**
* 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 WindowInsetsControllerCompat#controlWindowInsetsAnimation}.
*/
public final class WindowInsetsAnimationControllerCompat {
private final Impl mImpl;
WindowInsetsAnimationControllerCompat() {
if (Build.VERSION.SDK_INT < 30) {
mImpl = new Impl();
} else {
throw new UnsupportedOperationException("On API 30+, the constructor taking a "
+ WindowInsetsAnimationController.class.getSimpleName() + " as parameter");
}
}
@RequiresApi(30)
WindowInsetsAnimationControllerCompat(
@NonNull WindowInsetsAnimationController controller) {
mImpl = new Impl30(controller);
}
/**
* 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 WindowInsetsAnimationCompat.BoundsCompat#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 WindowInsetsAnimationCompat.BoundsCompat#getLowerBound()
*/
@NonNull
public Insets getHiddenStateInsets() {
return mImpl.getHiddenStateInsets();
}
/**
* Retrieves the {@link Insets} when the windows this animation is
* controlling are fully shown.
* <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 WindowInsetsAnimationCompat.BoundsCompat#getUpperBound()} that is being passed
* into the root view of hierarchy.
*
* @return Insets when the windows this animation is controlling are fully shown.
* @see WindowInsetsAnimationCompat.BoundsCompat#getUpperBound()
*/
@NonNull
public Insets getShownStateInsets() {
return mImpl.getShownStateInsets();
}
/**
* 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
public Insets getCurrentInsets() {
return mImpl.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)
public float getCurrentFraction() {
return mImpl.getCurrentFraction();
}
/**
* Current alpha value of the window.
*
* @return float value between 0 and 1.
*/
public float getCurrentAlpha() {
return mImpl.getCurrentAlpha();
}
/**
* @return The {@link Type}s this object is currently controlling.
*/
@InsetsType
public int getTypes() {
return mImpl.getTypes();
}
/**
* 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 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 WindowInsetsAnimationCompat.Callback} by calling
*
* {@link ViewCompat#setWindowInsetsAnimationCallback(View,
* WindowInsetsAnimationCompat.Callback)}
* that will be notified about any insets change via
* {@link WindowInsetsAnimationCompat.Callback#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}.
* 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 WindowInsetsAnimationCompat.Callback}.
* @see WindowInsetsAnimationCompat.Callback
* @see ViewCompat#setWindowInsetsAnimationCallback(View, WindowInsetsAnimationCompat.Callback)
*/
public void setInsetsAndAlpha(@Nullable Insets insets,
@FloatRange(from = 0f, to = 1f) float alpha,
@FloatRange(from = 0f, to = 1f) float fraction) {
mImpl.setInsetsAndAlpha(insets, alpha, fraction);
}
/**
* Finishes the animation, and leaves the windows shown or hidden.
* <p>
* After invoking {@link #finish}, this instance is no longer {@link #isReady ready}.
* <p>
* Note: Finishing an animation implicitly {@link #setInsetsAndAlpha sets insets and alpha}
* according to the requested end state without any further animation.
*
* @param shown if {@code true}, the windows will be shown after finishing the
* animation. Otherwise they will be hidden.
*/
public void finish(boolean shown) {
mImpl.finish(shown);
}
/**
* Returns whether this instance is ready to be used to control window insets.
* <p>
* Instances are ready when passed in {@link WindowInsetsAnimationControlListenerCompat#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.
*/
public boolean isReady() {
return !isFinished() && !isCancelled();
}
/**
* Returns whether this instance has been finished by a call to {@link #finish}.
*
* @return {@code true} if the instance is finished, {@code false} otherwise.
* @see WindowInsetsAnimationControlListenerCompat#onFinished
*/
public boolean isFinished() {
return mImpl.isFinished();
}
/**
* Returns whether this instance has been cancelled by the system, or by invoking the
* {@link android.os.CancellationSignal} passed into
* {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}.
*
* @return {@code true} if the instance is cancelled, {@code false} otherwise.
* @see WindowInsetsAnimationControlListenerCompat#onCancelled
*/
public boolean isCancelled() {
return mImpl.isCancelled();
}
private static class Impl {
Impl() {
//privatex
}
@NonNull
public Insets getHiddenStateInsets() {
return Insets.NONE;
}
@NonNull
public Insets getShownStateInsets() {
return Insets.NONE;
}
@NonNull
public Insets getCurrentInsets() {
return Insets.NONE;
}
@FloatRange(from = 0f, to = 1f)
public float getCurrentFraction() {
return 0f;
}
public float getCurrentAlpha() {
return 0f;
}
@InsetsType
public int getTypes() {
return 0;
}
public void setInsetsAndAlpha(@Nullable Insets insets,
@FloatRange(from = 0f, to = 1f) float alpha,
@FloatRange(from = 0f, to = 1f) float fraction) {
}
void finish(boolean shown) {
}
public boolean isReady() {
return false;
}
boolean isFinished() {
return false;
}
boolean isCancelled() {
return true;
}
}
@RequiresApi(30)
private static class Impl30 extends Impl {
private final WindowInsetsAnimationController mController;
Impl30(@NonNull WindowInsetsAnimationController controller) {
mController = controller;
}
@NonNull
@Override
public Insets getHiddenStateInsets() {
return Insets.toCompatInsets(mController.getHiddenStateInsets());
}
@NonNull
@Override
public Insets getShownStateInsets() {
return Insets.toCompatInsets(mController.getShownStateInsets());
}
@NonNull
@Override
public Insets getCurrentInsets() {
return Insets.toCompatInsets(mController.getCurrentInsets());
}
@Override
public float getCurrentFraction() {
return mController.getCurrentFraction();
}
@Override
public float getCurrentAlpha() {
return mController.getCurrentAlpha();
}
@Override
public int getTypes() {
return mController.getTypes();
}
@Override
public void setInsetsAndAlpha(@Nullable Insets insets, float alpha, float fraction) {
mController.setInsetsAndAlpha(insets == null ? null : insets.toPlatformInsets(),
alpha,
fraction
);
}
@Override
void finish(boolean shown) {
mController.finish(shown);
}
@Override
public boolean isReady() {
return mController.isReady();
}
@Override
boolean isFinished() {
return mController.isFinished();
}
@Override
boolean isCancelled() {
return mController.isCancelled();
}
}
}