public final class

WindowInsetsControllerCompat

extends java.lang.Object

 java.lang.Object

↳androidx.core.view.WindowInsetsControllerCompat

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Overview

Provide simple controls of windows that generate insets. For SDKs >= 30, this class is a simple wrapper around WindowInsetsController. For lower SDKs, this class aims to behave as close as possible to the original implementation.

Summary

Fields
public static final intBEHAVIOR_DEFAULT

The default option for WindowInsetsControllerCompat.setSystemBarsBehavior(int): Window would like to remain interactive when hiding navigation bars by calling WindowInsetsControllerCompat.hide(int) or WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float).

public static final intBEHAVIOR_SHOW_BARS_BY_SWIPE

Option for WindowInsetsControllerCompat.setSystemBarsBehavior(int): Window would like to remain interactive when hiding navigation bars by calling WindowInsetsControllerCompat.hide(int) or WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float).

public static final intBEHAVIOR_SHOW_BARS_BY_TOUCH

Option for WindowInsetsControllerCompat.setSystemBarsBehavior(int).

public static final intBEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

Option for WindowInsetsControllerCompat.setSystemBarsBehavior(int): Window would like to remain interactive when hiding navigation bars by calling WindowInsetsControllerCompat.hide(int) or WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float).

Constructors
publicWindowInsetsControllerCompat(Window window, View view)

Methods
public voidaddOnControllableInsetsChangedListener(WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener)

Adds a to the window insets controller.

public voidcontrolWindowInsetsAnimation(int types, long durationMillis, Interpolator interpolator, CancellationSignal cancellationSignal, WindowInsetsAnimationControlListenerCompat listener)

Lets the application control window inset animations in a frame-by-frame manner by modifying the position of the windows in the system causing insets directly using WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float) in the controller provided by the given listener.

public intgetSystemBarsBehavior()

Retrieves the requested behavior of system bars.

public voidhide(int types)

Makes a set of windows causing insets disappear.

public booleanisAppearanceLightNavigationBars()

Checks if the foreground of the navigation bar is set to light.

public booleanisAppearanceLightStatusBars()

Checks if the foreground of the status bar is set to light.

public voidremoveOnControllableInsetsChangedListener(WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener)

Removes a from the window insets controller.

public voidsetAppearanceLightNavigationBars(boolean isLight)

If true, changes the foreground color of the navigation bars to light so that the items on the bar can be read clearly.

public voidsetAppearanceLightStatusBars(boolean isLight)

If true, changes the foreground color of the status bars to light so that the items on the bar can be read clearly.

public voidsetSystemBarsBehavior(int behavior)

Controls the behavior of system bars.

public voidshow(int types)

Makes a set of windows that cause insets appear on screen.

public static WindowInsetsControllerCompattoWindowInsetsControllerCompat(WindowInsetsController insetsController)

Wrap a WindowInsetsController into a WindowInsetsControllerCompat for compatibility purpose.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH

Deprecated: This is not supported on Android and later. Use WindowInsetsControllerCompat.BEHAVIOR_DEFAULT or WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE instead.

Option for WindowInsetsControllerCompat.setSystemBarsBehavior(int). System bars will be forcibly shown on any user interaction on the corresponding display if navigation bars are hidden by WindowInsetsControllerCompat.hide(int) or WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float).

public static final int BEHAVIOR_DEFAULT

The default option for WindowInsetsControllerCompat.setSystemBarsBehavior(int): Window would like to remain interactive when hiding navigation bars by calling WindowInsetsControllerCompat.hide(int) or WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float).

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.

When the gesture navigation is enabled, the system gestures can be triggered regardless the visibility of system bars.

public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE

Deprecated: Use WindowInsetsControllerCompat.BEHAVIOR_DEFAULT instead.

Option for WindowInsetsControllerCompat.setSystemBarsBehavior(int): Window would like to remain interactive when hiding navigation bars by calling WindowInsetsControllerCompat.hide(int) or WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float).

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.

public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

Option for WindowInsetsControllerCompat.setSystemBarsBehavior(int): Window would like to remain interactive when hiding navigation bars by calling WindowInsetsControllerCompat.hide(int) or WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float).

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.

Constructors

public WindowInsetsControllerCompat(Window window, View view)

Methods

public static WindowInsetsControllerCompat toWindowInsetsControllerCompat(WindowInsetsController insetsController)

Deprecated: Use WindowCompat.getInsetsController(Window, View) instead

Wrap a WindowInsetsController into a WindowInsetsControllerCompat for compatibility purpose.

Parameters:

insetsController: The WindowInsetsController to wrap.

Returns:

The provided WindowInsetsController wrapped into a WindowInsetsControllerCompat

public void show(int types)

Makes a set of windows that cause insets appear on screen.

Note that if the window currently doesn't have control over a certain type, it will apply the change as soon as the window gains control. The app can listen to the event by observing View and checking visibility with WindowInsets.

Parameters:

types: A bitmask of WindowInsetsCompat.Type specifying what windows the app would like to make appear on screen.

public void hide(int types)

Makes a set of windows causing insets disappear.

Note that if the window currently doesn't have control over a certain type, it will apply the change as soon as the window gains control. The app can listen to the event by observing View and checking visibility with WindowInsets.

Parameters:

types: A bitmask of WindowInsetsCompat.Type specifying what windows the app would like to make disappear.

public boolean isAppearanceLightStatusBars()

Checks if the foreground of the status bar is set to light.

This method always returns false on API < 23.

If this value is being set in the theme (via ), then the correct value will only be returned once attached to the window.

Once this method is called, modifying `systemUiVisibility` directly to change the appearance is undefined behavior.

Returns:

true if the foreground is light

See also: WindowInsetsControllerCompat.setAppearanceLightStatusBars(boolean)

public void setAppearanceLightStatusBars(boolean isLight)

If true, changes the foreground color of the status bars to light so that the items on the bar can be read clearly. If false, reverts to the default appearance.

This method has no effect on API < 23.

Once this method is called, modifying `systemUiVisibility` directly to change the appearance is undefined behavior.

See also: WindowInsetsControllerCompat.isAppearanceLightStatusBars()

public boolean isAppearanceLightNavigationBars()

Checks if the foreground of the navigation bar is set to light.

This method always returns false on API < 26.

If this value is being set in the theme (via ), then the correct value will only be returned once attached to the window.

Once this method is called, modifying `systemUiVisibility` directly to change the appearance is undefined behavior.

Returns:

true if the foreground is light

See also: WindowInsetsControllerCompat.setAppearanceLightNavigationBars(boolean)

public void setAppearanceLightNavigationBars(boolean isLight)

If true, changes the foreground color of the navigation bars to light so that the items on the bar can be read clearly. If false, reverts to the default appearance.

This method has no effect on API < 26.

Once this method is called, modifying `systemUiVisibility` directly to change the appearance is undefined behavior.

See also: WindowInsetsControllerCompat.isAppearanceLightNavigationBars()

public void controlWindowInsetsAnimation(int types, long durationMillis, Interpolator interpolator, CancellationSignal cancellationSignal, WindowInsetsAnimationControlListenerCompat listener)

Lets the application control window inset animations in a frame-by-frame manner by modifying the position of the windows in the system causing insets directly using WindowInsetsAnimationControllerCompat.setInsetsAndAlpha(Insets, float, float) in the controller provided by the given listener.

This method only works on API >= 30 since there is no way to control the window in the system on prior APIs.

Parameters:

types: The WindowInsetsCompat.Types the application has requested to control.
durationMillis: Duration of animation in MILLISECONDS, or -1 if the animation doesn't have a predetermined duration. This value will be passed to WindowInsetsAnimationCompat.getDurationMillis()
interpolator: The interpolator used for this animation, or null if this animation doesn't follow an interpolation curve. This value will be passed to WindowInsetsAnimationCompat.getInterpolator() and used to calculate WindowInsetsAnimationCompat.getInterpolatedFraction().
cancellationSignal: A cancellation signal that the caller can use to cancel the request to obtain control, or once they have control, to cancel the control.
listener: The that gets called when the windows are ready to be controlled, among other callbacks.

See also: WindowInsetsAnimationCompat.getFraction(), WindowInsetsAnimationCompat.getInterpolatedFraction(), WindowInsetsAnimationCompat.getInterpolator(), WindowInsetsAnimationCompat.getDurationMillis()

public void setSystemBarsBehavior(int behavior)

Controls the behavior of system bars.

Parameters:

behavior: Determines how the bars behave when being hidden by the application.

See also: WindowInsetsControllerCompat.getSystemBarsBehavior()

public int getSystemBarsBehavior()

Retrieves the requested behavior of system bars.

Returns:

the system bar behavior controlled by this window.

See also: WindowInsetsControllerCompat.setSystemBarsBehavior(int)

public void addOnControllableInsetsChangedListener(WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener)

Adds a to the window insets controller.

Parameters:

listener: The listener to add.

See also: WindowInsetsControllerCompat.OnControllableInsetsChangedListener, WindowInsetsControllerCompat.removeOnControllableInsetsChangedListener(WindowInsetsControllerCompat.OnControllableInsetsChangedListener)

public void removeOnControllableInsetsChangedListener(WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener)

Removes a from the window insets controller.

Parameters:

listener: The listener to remove.

See also: WindowInsetsControllerCompat.OnControllableInsetsChangedListener, WindowInsetsControllerCompat.addOnControllableInsetsChangedListener(WindowInsetsControllerCompat.OnControllableInsetsChangedListener)

Source

/*
 * 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 static android.os.Build.VERSION.SDK_INT;

import android.annotation.SuppressLint;
import android.inputmethodservice.InputMethodService;
import android.os.CancellationSignal;
import android.view.View;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.view.animation.Interpolator;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.collection.SimpleArrayMap;
import androidx.core.graphics.Insets;
import androidx.core.view.WindowInsetsCompat.Type.InsetsType;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.TimeUnit;

/**
 * Provide simple controls of windows that generate insets.
 *
 * For SDKs >= 30, this class is a simple wrapper around {@link WindowInsetsController}. For
 * lower SDKs, this class aims to behave as close as possible to the original implementation.
 */
public final class WindowInsetsControllerCompat {

    /**
     * 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 WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(Insets, float, float)}.
     *
     * @deprecated This is not supported on Android {@link android.os.Build.VERSION_CODES#S} and
     * later. Use {@link #BEHAVIOR_DEFAULT} or {@link #BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE}
     * instead.
     */
    @Deprecated
    public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0;

    /**
     * The default option for {@link #setSystemBarsBehavior(int)}: Window would like to remain
     * interactive when hiding navigation bars by calling {@link #hide(int)} or
     * {@link WindowInsetsAnimationControllerCompat#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>
     *
     * <p>When the gesture navigation is enabled, the system gestures can be triggered regardless
     * the visibility of system bars.</p>
     */
    public static final int BEHAVIOR_DEFAULT = 1;

    /**
     * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive
     * when hiding navigation bars by calling {@link #hide(int)} or
     * {@link WindowInsetsAnimationControllerCompat#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.
     *
     * @deprecated Use {@link #BEHAVIOR_DEFAULT} instead.
     */
    @Deprecated
    public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = BEHAVIOR_DEFAULT;

    /**
     * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain
     * interactive when hiding navigation bars by calling {@link #hide(int)} or
     * {@link WindowInsetsAnimationControllerCompat#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.
     */
    public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2;

    private final Impl mImpl;

    /**
     * This version fails to workaround
     * <a href="https://issuetracker.google.com/issues/180881870">
     *     https://issuetracker.google.com/issues/180881870
     * </a>, but is present for backwards compatibility.
     */
    @RequiresApi(30)
    @Deprecated
    private WindowInsetsControllerCompat(@NonNull WindowInsetsController insetsController) {
        if (SDK_INT >= 35) {
            mImpl = new Impl35(insetsController,
                    this,
                    new SoftwareKeyboardControllerCompat(insetsController));
        } else {
            mImpl = new Impl30(insetsController,
                    this,
                    new SoftwareKeyboardControllerCompat(insetsController));
        }
    }

    public WindowInsetsControllerCompat(@NonNull Window window, @NonNull View view) {
        SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat =
                new SoftwareKeyboardControllerCompat(view);
        if (SDK_INT >= 35) {
            mImpl = new Impl35(window, this, softwareKeyboardControllerCompat);
        } else if (SDK_INT >= 30) {
            mImpl = new Impl30(window, this, softwareKeyboardControllerCompat);
        } else if (SDK_INT >= 26) {
            mImpl = new Impl26(window, softwareKeyboardControllerCompat);
        } else if (SDK_INT >= 23) {
            mImpl = new Impl23(window, softwareKeyboardControllerCompat);
        } else if (SDK_INT >= 20) {
            mImpl = new Impl20(window, softwareKeyboardControllerCompat);
        } else {
            mImpl = new Impl();
        }
    }

    /**
     * Wrap a {@link WindowInsetsController} into a {@link WindowInsetsControllerCompat} for
     * compatibility purpose.
     *
     * @param insetsController The {@link WindowInsetsController} to wrap.
     * @return The provided {@link WindowInsetsController} wrapped into a
     * {@link WindowInsetsControllerCompat}
     * @deprecated Use {@link WindowCompat#getInsetsController(Window, View)} instead
     */
    @NonNull
    @RequiresApi(30)
    @Deprecated
    public static WindowInsetsControllerCompat toWindowInsetsControllerCompat(
            @NonNull WindowInsetsController insetsController) {
        return new WindowInsetsControllerCompat(insetsController);
    }

    /**
     * Determines the behavior of system bars when hiding them by calling {@link #hide}.
     *
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(value = {BEHAVIOR_DEFAULT, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE})
    @interface Behavior {
    }

    /**
     * Makes a set of windows that cause insets appear on screen.
     * <p>
     * Note that if the window currently doesn't have control over a certain type, it will apply the
     * change as soon as the window gains control. The app can listen to the event by observing
     * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
     *
     * @param types A bitmask of {@link WindowInsetsCompat.Type} specifying what windows the app
     *              would like to make appear on screen.
     */
    public void show(@InsetsType int types) {
        mImpl.show(types);
    }

    /**
     * Makes a set of windows causing insets disappear.
     * <p>
     * Note that if the window currently doesn't have control over a certain type, it will apply the
     * change as soon as the window gains control. The app can listen to the event by observing
     * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
     *
     * @param types A bitmask of {@link WindowInsetsCompat.Type} specifying what windows the app
     *              would like to make disappear.
     */
    public void hide(@InsetsType int types) {
        mImpl.hide(types);
    }


    /**
     * Checks if the foreground of the status bar is set to light.
     * <p>
     * This method always returns false on API < 23.
     * <p>
     * If this value is being set in the theme (via {@link android.R.attr#windowLightStatusBar}),
     * then the correct value will only be returned once attached to the window.
     * <p>
     * Once this method is called, modifying `systemUiVisibility` directly to change the
     * appearance is undefined behavior.
     *
     * @return true if the foreground is light
     * @see #setAppearanceLightStatusBars(boolean)
     */
    public boolean isAppearanceLightStatusBars() {
        return mImpl.isAppearanceLightStatusBars();
    }

    /**
     * If true, changes the foreground color of the status bars to light so that the items on the
     * bar can be read clearly. If false, reverts to the default appearance.
     * <p>
     * This method has no effect on API < 23.
     * <p>
     * Once this method is called, modifying `systemUiVisibility` directly to change the
     * appearance is undefined behavior.
     *
     * @see #isAppearanceLightStatusBars()
     */
    public void setAppearanceLightStatusBars(boolean isLight) {
        mImpl.setAppearanceLightStatusBars(isLight);
    }

    /**
     * Checks if the foreground of the navigation bar is set to light.
     * <p>
     * This method always returns false on API < 26.
     * <p>
     * If this value is being set in the theme (via
     * {@link android.R.attr#windowLightNavigationBar}),
     * then the correct value will only be returned once attached to the window.
     * <p>
     * Once this method is called, modifying `systemUiVisibility` directly to change the
     * appearance is undefined behavior.
     *
     * @return true if the foreground is light
     * @see #setAppearanceLightNavigationBars(boolean)
     */
    public boolean isAppearanceLightNavigationBars() {
        return mImpl.isAppearanceLightNavigationBars();
    }

    /**
     * If true, changes the foreground color of the navigation bars to light so that the items on
     * the bar can be read clearly. If false, reverts to the default appearance.
     * <p>
     * This method has no effect on API < 26.
     * <p>
     * Once this method is called, modifying `systemUiVisibility` directly to change the
     * appearance is undefined behavior.
     *
     * @see #isAppearanceLightNavigationBars()
     */
    public void setAppearanceLightNavigationBars(boolean isLight) {
        mImpl.setAppearanceLightNavigationBars(isLight);
    }

    /**
     * Lets the application control window inset animations in a frame-by-frame manner by
     * modifying the position of the windows in the system causing insets directly using
     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha} in the controller provided
     * by the given listener.
     * <p>
     * This method only works on API >= 30 since there is no way to control the window in the
     * system on prior APIs.
     *
     * @param types              The {@link WindowInsetsCompat.Type}s the application has
     *                           requested to control.
     * @param durationMillis     Duration of animation in {@link TimeUnit#MILLISECONDS}, or -1 if
     *                           the animation doesn't have a predetermined duration. This value
     *                           will be passed to
     *                           {@link WindowInsetsAnimationCompat#getDurationMillis()}
     * @param interpolator       The interpolator used for this animation, or {@code null } if
     *                           this animation doesn't follow an interpolation curve. This value
     *                           will be passed to
     *                           {@link WindowInsetsAnimationCompat#getInterpolator()} and used
     *                           to calculate
     *                           {@link WindowInsetsAnimationCompat#getInterpolatedFraction()}.
     * @param cancellationSignal A cancellation signal that the caller can use to cancel the
     *                           request to obtain control, or once they have control, to cancel
     *                           the control.
     * @param listener           The {@link WindowInsetsAnimationControlListener} that gets
     *                           called when the windows are ready to be controlled, among other
     *                           callbacks.
     * @see WindowInsetsAnimationCompat#getFraction()
     * @see WindowInsetsAnimationCompat#getInterpolatedFraction()
     * @see WindowInsetsAnimationCompat#getInterpolator()
     * @see WindowInsetsAnimationCompat#getDurationMillis()
     */
    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
            @Nullable Interpolator interpolator,
            @Nullable CancellationSignal cancellationSignal,
            @NonNull WindowInsetsAnimationControlListenerCompat listener) {
        mImpl.controlWindowInsetsAnimation(types,
                durationMillis,
                interpolator,
                cancellationSignal,
                listener);
    }

    /**
     * Controls the behavior of system bars.
     *
     * @param behavior Determines how the bars behave when being hidden by the application.
     * @see #getSystemBarsBehavior
     */
    public void setSystemBarsBehavior(@Behavior int behavior) {
        mImpl.setSystemBarsBehavior(behavior);
    }

    /**
     * Retrieves the requested behavior of system bars.
     *
     * @return the system bar behavior controlled by this window.
     * @see #setSystemBarsBehavior(int)
     */
    @SuppressLint("WrongConstant")
    @Behavior
    public int getSystemBarsBehavior() {
        return mImpl.getSystemBarsBehavior();
    }

    /**
     * Adds a {@link WindowInsetsController.OnControllableInsetsChangedListener} to the window
     * insets controller.
     *
     * @param listener The listener to add.
     * @see WindowInsetsControllerCompat.OnControllableInsetsChangedListener
     * @see #removeOnControllableInsetsChangedListener(
     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
     */
    public void addOnControllableInsetsChangedListener(
            @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener) {
        mImpl.addOnControllableInsetsChangedListener(listener);
    }

    /**
     * Removes a {@link WindowInsetsController.OnControllableInsetsChangedListener} from the
     * window insets controller.
     *
     * @param listener The listener to remove.
     * @see WindowInsetsControllerCompat.OnControllableInsetsChangedListener
     * @see #addOnControllableInsetsChangedListener(
     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
     */
    public void removeOnControllableInsetsChangedListener(
            @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
                    listener) {
        mImpl.removeOnControllableInsetsChangedListener(listener);
    }

    /**
     * Listener to be notified when the set of controllable {@link WindowInsetsCompat.Type}
     * controlled by a {@link WindowInsetsController} changes.
     * <p>
     * Once a {@link WindowInsetsCompat.Type} becomes controllable, the app will be able to
     * control the window that is causing this type of insets by calling
     * {@link #controlWindowInsetsAnimation}.
     * <p>
     * Note: When listening to cancellability of the {@link WindowInsets.Type#ime},
     * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService}
     * decides to cancel the show request. This could happen when there is a hardware keyboard
     * attached.
     *
     * @see #addOnControllableInsetsChangedListener(
     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
     * @see #removeOnControllableInsetsChangedListener(
     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
     */
    public interface OnControllableInsetsChangedListener {

        /**
         * Called when the set of controllable {@link WindowInsetsCompat.Type} changes.
         *
         * @param controller The controller for which the set of controllable
         *                   {@link WindowInsetsCompat.Type}s
         *                   are changing.
         * @param typeMask   Bitwise behavior type-mask of the {@link WindowInsetsCompat.Type}s
         *                   the controller is currently able to control.
         */
        void onControllableInsetsChanged(@NonNull WindowInsetsControllerCompat controller,
                @InsetsType int typeMask);
    }

    private static class Impl {
        static final int KEY_BEHAVIOR = 356039078;

        Impl() {
            //private
        }

        void show(int types) {
        }

        void hide(int types) {
        }

        void controlWindowInsetsAnimation(int types, long durationMillis,
                Interpolator interpolator, CancellationSignal cancellationSignal,
                WindowInsetsAnimationControlListenerCompat listener) {
        }

        void setSystemBarsBehavior(int behavior) {
        }

        int getSystemBarsBehavior() {
            return BEHAVIOR_DEFAULT;
        }

        public boolean isAppearanceLightStatusBars() {
            return false;
        }

        public void setAppearanceLightStatusBars(boolean isLight) {
        }

        public boolean isAppearanceLightNavigationBars() {
            return false;
        }

        public void setAppearanceLightNavigationBars(boolean isLight) {
        }

        void addOnControllableInsetsChangedListener(
                WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener) {
        }

        void removeOnControllableInsetsChangedListener(
                @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
                        listener) {
        }
    }

    @RequiresApi(20)
    private static class Impl20 extends Impl {

        @NonNull
        protected final Window mWindow;

        @NonNull
        private final SoftwareKeyboardControllerCompat mSoftwareKeyboardControllerCompat;

        Impl20(@NonNull Window window,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            mWindow = window;
            mSoftwareKeyboardControllerCompat = softwareKeyboardControllerCompat;
        }

        @Override
        void show(int typeMask) {
            for (int i = WindowInsetsCompat.Type.FIRST; i <= WindowInsetsCompat.Type.LAST;
                    i = i << 1) {
                if ((typeMask & i) == 0) {
                    continue;
                }
                showForType(i);
            }
        }

        private void showForType(int type) {
            switch (type) {
                case WindowInsetsCompat.Type.STATUS_BARS:
                    unsetSystemUiFlag(View.SYSTEM_UI_FLAG_FULLSCREEN);
                    unsetWindowFlag(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                    return;
                case WindowInsetsCompat.Type.NAVIGATION_BARS:
                    unsetSystemUiFlag(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
                    return;
                case WindowInsetsCompat.Type.IME:
                    mSoftwareKeyboardControllerCompat.show();
            }
        }

        @Override
        void hide(int typeMask) {
            for (int i = WindowInsetsCompat.Type.FIRST; i <= WindowInsetsCompat.Type.LAST;
                    i = i << 1) {
                if ((typeMask & i) == 0) {
                    continue;
                }
                hideForType(i);
            }
        }

        private void hideForType(int type) {
            switch (type) {
                case WindowInsetsCompat.Type.STATUS_BARS:
                    setSystemUiFlag(View.SYSTEM_UI_FLAG_FULLSCREEN);
                    return;
                case WindowInsetsCompat.Type.NAVIGATION_BARS:
                    setSystemUiFlag(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
                    return;
                case WindowInsetsCompat.Type.IME:
                    mSoftwareKeyboardControllerCompat.hide();
            }
        }

        protected void setSystemUiFlag(int systemUiFlag) {
            View decorView = mWindow.getDecorView();
            decorView.setSystemUiVisibility(
                    decorView.getSystemUiVisibility()
                            | systemUiFlag);
        }

        protected void unsetSystemUiFlag(int systemUiFlag) {
            View decorView = mWindow.getDecorView();
            decorView.setSystemUiVisibility(
                    decorView.getSystemUiVisibility()
                            & ~systemUiFlag);
        }

        protected void setWindowFlag(int windowFlag) {
            mWindow.addFlags(windowFlag);
        }

        protected void unsetWindowFlag(int windowFlag) {
            mWindow.clearFlags(windowFlag);
        }

        @Override
        void controlWindowInsetsAnimation(int types, long durationMillis,
                Interpolator interpolator, CancellationSignal cancellationSignal,
                WindowInsetsAnimationControlListenerCompat listener) {
        }

        @Override
        void setSystemBarsBehavior(int behavior) {
            mWindow.getDecorView().setTag(KEY_BEHAVIOR, behavior);
            switch (behavior) {
                case BEHAVIOR_DEFAULT:
                    unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
                    setSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE);
                    break;
                case BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE:
                    unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE);
                    setSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
                    break;
                case BEHAVIOR_SHOW_BARS_BY_TOUCH:
                    unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
                    break;
            }
        }

        @Override
        int getSystemBarsBehavior() {
            final Object behaviorTag = mWindow.getDecorView().getTag(KEY_BEHAVIOR);
            return behaviorTag != null ? (int) behaviorTag : BEHAVIOR_DEFAULT;
        }

        @Override
        void addOnControllableInsetsChangedListener(
                WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener) {
        }

        @Override
        void removeOnControllableInsetsChangedListener(
                @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
                        listener) {
        }
    }

    @RequiresApi(23)
    private static class Impl23 extends Impl20 {

        Impl23(@NonNull Window window,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            super(window, softwareKeyboardControllerCompat);
        }

        @Override
        public boolean isAppearanceLightStatusBars() {
            return (mWindow.getDecorView().getSystemUiVisibility()
                    & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
        }

        @Override
        public void setAppearanceLightStatusBars(boolean isLight) {
            if (isLight) {
                unsetWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                setWindowFlag(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                setSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            } else {
                unsetSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            }
        }
    }

    @RequiresApi(26)
    private static class Impl26 extends Impl23 {

        Impl26(@NonNull Window window,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            super(window, softwareKeyboardControllerCompat);
        }

        @Override
        public boolean isAppearanceLightNavigationBars() {
            return (mWindow.getDecorView().getSystemUiVisibility()
                    & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;
        }

        @Override
        public void setAppearanceLightNavigationBars(boolean isLight) {
            if (isLight) {
                unsetWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
                setWindowFlag(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                setSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
            } else {
                unsetSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
            }
        }
    }

    @RequiresApi(30)
    private static class Impl30 extends Impl {

        final WindowInsetsControllerCompat mCompatController;
        final WindowInsetsController mInsetsController;
        final SoftwareKeyboardControllerCompat mSoftwareKeyboardControllerCompat;
        private final SimpleArrayMap<
                WindowInsetsControllerCompat.OnControllableInsetsChangedListener,
                WindowInsetsController.OnControllableInsetsChangedListener>
                mListeners = new SimpleArrayMap<>();

        protected Window mWindow;

        Impl30(@NonNull Window window,
                @NonNull WindowInsetsControllerCompat compatController,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            this(window.getInsetsController(), compatController, softwareKeyboardControllerCompat);
            mWindow = window;
        }

        Impl30(@NonNull WindowInsetsController insetsController,
                @NonNull WindowInsetsControllerCompat compatController,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            mInsetsController = insetsController;
            mCompatController = compatController;
            mSoftwareKeyboardControllerCompat = softwareKeyboardControllerCompat;
        }

        @Override
        void show(@InsetsType int types) {
            if ((types & WindowInsetsCompat.Type.IME) != 0) {
                mSoftwareKeyboardControllerCompat.show();
            }
            mInsetsController.show(types & ~WindowInsetsCompat.Type.IME);
        }

        @Override
        void hide(@InsetsType int types) {
            if ((types & WindowInsetsCompat.Type.IME) != 0) {
                mSoftwareKeyboardControllerCompat.hide();
            }
            mInsetsController.hide(types & ~WindowInsetsCompat.Type.IME);
        }

        @Override
        public boolean isAppearanceLightStatusBars() {
            // This is a side-effectful workaround
            // Because the mask is zero, this won't change the system bar appearance
            // However, it "unlocks" reading the effective system bar appearance in the following
            // call. Without this being "unlocked," the system bar appearance will always return
            // nothing, even if it has been set in the theme or by the system ui flags before
            // querying for it.
            mInsetsController.setSystemBarsAppearance(0, 0);
            return (mInsetsController.getSystemBarsAppearance()
                    & WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS) != 0;
        }

        @Override
        public void setAppearanceLightStatusBars(boolean isLight) {
            if (isLight) {
                if (mWindow != null) {
                    setSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                }

                mInsetsController.setSystemBarsAppearance(
                        WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
                        WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
            } else {
                if (mWindow != null) {
                    unsetSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                }

                mInsetsController.setSystemBarsAppearance(
                        0,
                        WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
            }
        }

        @Override
        public boolean isAppearanceLightNavigationBars() {
            // This is a side-effectful workaround
            // Because the mask is zero, this won't change the system bar appearance
            // However, it "unlocks" reading the effective system bar appearance in the following
            // call. Without this being "unlocked," the system bar appearance will always return
            // nothing, even if it has been set in the theme or by the system ui flags before
            // querying for it.
            mInsetsController.setSystemBarsAppearance(0, 0);
            return (mInsetsController.getSystemBarsAppearance()
                    & WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
        }

        @Override
        public void setAppearanceLightNavigationBars(boolean isLight) {
            if (isLight) {
                if (mWindow != null) {
                    setSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
                }

                mInsetsController.setSystemBarsAppearance(
                        WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS,
                        WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS);
            } else {
                if (mWindow != null) {
                    unsetSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
                }

                mInsetsController.setSystemBarsAppearance(
                        0,
                        WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS);
            }
        }

        @Override
        void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
                @Nullable Interpolator interpolator,
                @Nullable CancellationSignal cancellationSignal,
                @NonNull final WindowInsetsAnimationControlListenerCompat listener) {

            WindowInsetsAnimationControlListener fwListener =
                    new WindowInsetsAnimationControlListener() {

                        private WindowInsetsAnimationControllerCompat mCompatAnimController = null;

                        @Override
                        public void onReady(@NonNull WindowInsetsAnimationController controller,
                                int types) {
                            mCompatAnimController =
                                    new WindowInsetsAnimationControllerCompat(controller);
                            listener.onReady(mCompatAnimController, types);
                        }

                        @Override
                        public void onFinished(
                                @NonNull WindowInsetsAnimationController controller) {
                            listener.onFinished(mCompatAnimController);
                        }

                        @Override
                        public void onCancelled(
                                @Nullable WindowInsetsAnimationController controller) {
                            listener.onCancelled(controller == null ? null : mCompatAnimController);
                        }
                    };

            mInsetsController.controlWindowInsetsAnimation(types,
                    durationMillis,
                    interpolator,
                    cancellationSignal,
                    fwListener);
        }

        /**
         * Controls the behavior of system bars.
         *
         * @param behavior Determines how the bars behave when being hidden by the application.
         * @see #getSystemBarsBehavior
         */
        @Override
        void setSystemBarsBehavior(@Behavior int behavior) {
            if (mWindow != null) {
                // Use the legacy way to control the behavior as a workaround because API 30 has a
                // bug that the behavior might be cleared unexpectedly after setting a LayoutParam
                // to a window.
                mWindow.getDecorView().setTag(KEY_BEHAVIOR, behavior);
                switch (behavior) {
                    case BEHAVIOR_DEFAULT:
                        unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
                        setSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE);
                        break;
                    case BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE:
                        unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE);
                        setSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
                        break;
                    case BEHAVIOR_SHOW_BARS_BY_TOUCH:
                        unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE
                                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
                        break;
                }
            } else {
                mInsetsController.setSystemBarsBehavior(behavior);
            }
        }

        /**
         * Retrieves the requested behavior of system bars.
         *
         * @return the system bar behavior controlled by this window.
         * @see #setSystemBarsBehavior(int)
         */
        @SuppressLint("WrongConstant")
        @Override
        @Behavior
        int getSystemBarsBehavior() {
            if (mWindow != null) {
                final Object behaviorTag = mWindow.getDecorView().getTag(KEY_BEHAVIOR);
                return behaviorTag != null ? (int) behaviorTag : BEHAVIOR_DEFAULT;
            } else {
                return mInsetsController.getSystemBarsBehavior();
            }
        }

        @Override
        void addOnControllableInsetsChangedListener(
                @NonNull final WindowInsetsControllerCompat.OnControllableInsetsChangedListener
                        listener) {

            if (mListeners.containsKey(listener)) {
                // The listener has already been added.
                return;
            }
            WindowInsetsController.OnControllableInsetsChangedListener
                    fwListener = (controller, typeMask) -> {
                        if (mInsetsController == controller) {
                            listener.onControllableInsetsChanged(
                                    mCompatController, typeMask);
                        }
                    };
            mListeners.put(listener, fwListener);
            mInsetsController.addOnControllableInsetsChangedListener(fwListener);
        }

        @Override
        void removeOnControllableInsetsChangedListener(
                @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
                        listener) {
            WindowInsetsController.OnControllableInsetsChangedListener
                    fwListener = mListeners.remove(listener);
            if (fwListener != null) {
                mInsetsController.removeOnControllableInsetsChangedListener(fwListener);
            }
        }

        protected void unsetSystemUiFlag(int systemUiFlag) {
            View decorView = mWindow.getDecorView();
            decorView.setSystemUiVisibility(
                    decorView.getSystemUiVisibility()
                            & ~systemUiFlag);
        }

        protected void setSystemUiFlag(int systemUiFlag) {
            View decorView = mWindow.getDecorView();
            decorView.setSystemUiVisibility(
                    decorView.getSystemUiVisibility()
                            | systemUiFlag);
        }
    }

    @RequiresApi(31)
    private static class Impl31 extends Impl30 {

        Impl31(@NonNull Window window,
                @NonNull WindowInsetsControllerCompat compatController,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            super(window, compatController, softwareKeyboardControllerCompat);
        }

        Impl31(@NonNull WindowInsetsController insetsController,
                @NonNull WindowInsetsControllerCompat compatController,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            super(insetsController, compatController, softwareKeyboardControllerCompat);
        }

        /**
         * Controls the behavior of system bars.
         *
         * @param behavior Determines how the bars behave when being hidden by the application.
         * @see #getSystemBarsBehavior
         */
        @Override
        void setSystemBarsBehavior(@Behavior int behavior) {
            mInsetsController.setSystemBarsBehavior(behavior);
        }

        /**
         * Retrieves the requested behavior of system bars.
         *
         * @return the system bar behavior controlled by this window.
         * @see #setSystemBarsBehavior(int)
         */
        @SuppressLint("WrongConstant")
        @Override
        @Behavior
        int getSystemBarsBehavior() {
            return mInsetsController.getSystemBarsBehavior();
        }
    }

    @RequiresApi(35)
    private static class Impl35 extends Impl31 {

        Impl35(@NonNull Window window,
                @NonNull WindowInsetsControllerCompat compatController,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            super(window, compatController, softwareKeyboardControllerCompat);
        }

        Impl35(@NonNull WindowInsetsController insetsController,
                @NonNull WindowInsetsControllerCompat compatController,
                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
            super(insetsController, compatController, softwareKeyboardControllerCompat);
        }

        @Override
        public boolean isAppearanceLightStatusBars() {
            return (mInsetsController.getSystemBarsAppearance()
                    & WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS) != 0;
        }

        @Override
        public boolean isAppearanceLightNavigationBars() {
            return (mInsetsController.getSystemBarsAppearance()
                    & WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
        }

    }
}