public class

ActionBarOverlayLayout

extends ViewGroup

implements DecorContentParent, NestedScrollingParent, NestedScrollingParent2, NestedScrollingParent3

 java.lang.Object

↳ViewGroup

↳androidx.appcompat.widget.ActionBarOverlayLayout

Gradle dependencies

compile group: 'androidx.appcompat', name: 'appcompat', version: '1.6.0-alpha04'

  • groupId: androidx.appcompat
  • artifactId: appcompat
  • version: 1.6.0-alpha04

Artifact androidx.appcompat:appcompat:1.6.0-alpha04 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.appcompat:appcompat com.android.support:appcompat-v7

Androidx class mapping:

androidx.appcompat.widget.ActionBarOverlayLayout android.support.v7.widget.ActionBarOverlayLayout

Overview

Special layout for the containing of an overlay action bar (and its content) to correctly handle fitting system windows when the content has request that its layout ignore them.

Summary

Constructors
publicActionBarOverlayLayout(Context context)

publicActionBarOverlayLayout(Context context, AttributeSet attrs)

Methods
public booleancanShowOverflowMenu()

protected booleancheckLayoutParams(ViewGroup.LayoutParams p)

public voiddismissPopups()

public voiddraw(Canvas c)

protected booleanfitSystemWindows(Rect insets)

protected ActionBarOverlayLayout.LayoutParamsgenerateDefaultLayoutParams()

public ActionBarOverlayLayout.LayoutParamsgenerateLayoutParams(AttributeSet attrs)

protected ViewGroup.LayoutParamsgenerateLayoutParams(ViewGroup.LayoutParams p)

public intgetActionBarHideOffset()

public intgetNestedScrollAxes()

public java.lang.CharSequencegetTitle()

public booleanhasIcon()

public booleanhasLogo()

public booleanhideOverflowMenu()

public voidinitFeature(int windowFeature)

public booleanisHideOnContentScrollEnabled()

public booleanisInOverlayMode()

public booleanisOverflowMenuShowing()

public booleanisOverflowMenuShowPending()

public WindowInsetsonApplyWindowInsets(WindowInsets in)

protected voidonConfigurationChanged(Configuration newConfig)

protected voidonDetachedFromWindow()

protected voidonLayout(boolean changed, int left, int top, int right, int bottom)

protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

public booleanonNestedFling(View target, float velocityX, float velocityY, boolean consumed)

public booleanonNestedPreFling(View target, float velocityX, float velocityY)

public voidonNestedPreScroll(View target, int dx, int dy, int[] consumed[])

public voidonNestedPreScroll(View target, int dx, int dy, int[] consumed[], int type)

public voidonNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)

public voidonNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type)

public voidonNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, int[] consumed[])

public voidonNestedScrollAccepted(View child, View target, int axes)

public voidonNestedScrollAccepted(View child, View target, int axes, int type)

public booleanonStartNestedScroll(View child, View target, int axes)

public booleanonStartNestedScroll(View child, View target, int axes, int type)

public voidonStopNestedScroll(View target)

public voidonStopNestedScroll(View target, int type)

public voidonWindowSystemUiVisibilityChanged(int visible)

protected voidonWindowVisibilityChanged(int visibility)

public voidrestoreToolbarHierarchyState(<any> toolbarStates)

public voidsaveToolbarHierarchyState(<any> toolbarStates)

public voidsetActionBarHideOffset(int offset)

public voidsetActionBarVisibilityCallback(ActionBarOverlayLayout.ActionBarVisibilityCallback cb)

public voidsetHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs)

public voidsetHideOnContentScrollEnabled(boolean hideOnContentScroll)

public voidsetIcon(Drawable d)

public voidsetIcon(int resId)

public voidsetLogo(int resId)

public voidsetMenu(Menu menu, MenuPresenter.Callback cb)

public voidsetMenuPrepared()

public voidsetOverlayMode(boolean overlayMode)

public voidsetShowingForActionMode(boolean showing)

public voidsetUiOptions(int uiOptions)

public voidsetWindowCallback(Window.Callback cb)

public voidsetWindowTitle(java.lang.CharSequence title)

public booleanshouldDelayChildPressedState()

public booleanshowOverflowMenu()

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

Constructors

public ActionBarOverlayLayout(Context context)

public ActionBarOverlayLayout(Context context, AttributeSet attrs)

Methods

protected void onDetachedFromWindow()

public void setActionBarVisibilityCallback(ActionBarOverlayLayout.ActionBarVisibilityCallback cb)

public void setOverlayMode(boolean overlayMode)

public boolean isInOverlayMode()

public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs)

public void setShowingForActionMode(boolean showing)

protected void onConfigurationChanged(Configuration newConfig)

public void onWindowSystemUiVisibilityChanged(int visible)

Deprecated: see View

protected void onWindowVisibilityChanged(int visibility)

protected boolean fitSystemWindows(Rect insets)

public WindowInsets onApplyWindowInsets(WindowInsets in)

protected ActionBarOverlayLayout.LayoutParams generateDefaultLayoutParams()

public ActionBarOverlayLayout.LayoutParams generateLayoutParams(AttributeSet attrs)

protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p)

protected boolean checkLayoutParams(ViewGroup.LayoutParams p)

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

protected void onLayout(boolean changed, int left, int top, int right, int bottom)

public void draw(Canvas c)

public boolean shouldDelayChildPressedState()

public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, int[] consumed[])

public boolean onStartNestedScroll(View child, View target, int axes, int type)

public void onNestedScrollAccepted(View child, View target, int axes, int type)

public void onStopNestedScroll(View target, int type)

public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type)

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed[], int type)

public boolean onStartNestedScroll(View child, View target, int axes)

public void onNestedScrollAccepted(View child, View target, int axes)

public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)

public void onStopNestedScroll(View target)

public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed)

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed[])

public boolean onNestedPreFling(View target, float velocityX, float velocityY)

public int getNestedScrollAxes()

public void setHideOnContentScrollEnabled(boolean hideOnContentScroll)

public boolean isHideOnContentScrollEnabled()

public int getActionBarHideOffset()

public void setActionBarHideOffset(int offset)

public void setWindowCallback(Window.Callback cb)

public void setWindowTitle(java.lang.CharSequence title)

public java.lang.CharSequence getTitle()

public void initFeature(int windowFeature)

public void setUiOptions(int uiOptions)

public boolean hasIcon()

public boolean hasLogo()

public void setIcon(int resId)

public void setIcon(Drawable d)

public void setLogo(int resId)

public boolean canShowOverflowMenu()

public boolean isOverflowMenuShowing()

public boolean isOverflowMenuShowPending()

public boolean showOverflowMenu()

public boolean hideOverflowMenu()

public void setMenuPrepared()

public void setMenu(Menu menu, MenuPresenter.Callback cb)

public void saveToolbarHierarchyState(<any> toolbarStates)

public void restoreToolbarHierarchyState(<any> toolbarStates)

public void dismissPopups()

Source

/*
 * Copyright (C) 2012 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.appcompat.widget;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.Window;
import android.view.WindowInsets;
import android.widget.OverScroller;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.appcompat.R;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.view.menu.MenuPresenter;
import androidx.core.graphics.Insets;
import androidx.core.view.NestedScrollingParent;
import androidx.core.view.NestedScrollingParent2;
import androidx.core.view.NestedScrollingParent3;
import androidx.core.view.NestedScrollingParentHelper;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

/**
 * Special layout for the containing of an overlay action bar (and its content) to correctly handle
 * fitting system windows when the content has request that its layout ignore them.
 *
 * @hide
 */
@RestrictTo(LIBRARY_GROUP_PREFIX)
@SuppressLint("UnknownNullness") // this is not public API
public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent,
        NestedScrollingParent, NestedScrollingParent2, NestedScrollingParent3 {
    private static final String TAG = "ActionBarOverlayLayout";

    private int mActionBarHeight;
    //private WindowDecorActionBar mActionBar;
    private int mWindowVisibility = View.VISIBLE;

    // The main UI elements that we handle the layout of.
    private ContentFrameLayout mContent;
    ActionBarContainer mActionBarTop;

    // Some interior UI elements.
    private DecorToolbar mDecorToolbar;

    // Content overlay drawable - generally the action bar's shadow
    private Drawable mWindowContentOverlay;
    private boolean mIgnoreWindowContentOverlay;

    private boolean mOverlayMode;
    private boolean mHasNonEmbeddedTabs;
    private boolean mHideOnContentScroll;
    boolean mAnimatingForFling;
    private int mHideOnContentScrollReference;
    private int mLastSystemUiVisibility;
    private final Rect mBaseContentInsets = new Rect();
    private final Rect mLastBaseContentInsets = new Rect();
    private final Rect mContentInsets = new Rect();

    // Used on API < 21
    private final Rect mBaseInnerInsetsRect = new Rect();
    private final Rect mLastBaseInnerInsetsRect = new Rect();
    private final Rect mInnerInsetsRect = new Rect();
    private final Rect mLastInnerInsetsRect = new Rect();

    // Used on API 21+
    @NonNull private WindowInsetsCompat mBaseInnerInsets = WindowInsetsCompat.CONSUMED;
    @NonNull private WindowInsetsCompat mLastBaseInnerInsets = WindowInsetsCompat.CONSUMED;
    @NonNull private WindowInsetsCompat mInnerInsets = WindowInsetsCompat.CONSUMED;
    @NonNull private WindowInsetsCompat mLastInnerInsets = WindowInsetsCompat.CONSUMED;

    private ActionBarVisibilityCallback mActionBarVisibilityCallback;

    private static final int ACTION_BAR_ANIMATE_DELAY = 600; // ms

    private OverScroller mFlingEstimator;

    ViewPropertyAnimator mCurrentActionBarTopAnimator;

    final AnimatorListenerAdapter mTopAnimatorListener = new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animator) {
            mCurrentActionBarTopAnimator = null;
            mAnimatingForFling = false;
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            mCurrentActionBarTopAnimator = null;
            mAnimatingForFling = false;
        }
    };

    private final Runnable mRemoveActionBarHideOffset = new Runnable() {
        @Override
        public void run() {
            haltActionBarHideOffsetAnimations();
            mCurrentActionBarTopAnimator = mActionBarTop.animate().translationY(0)
                    .setListener(mTopAnimatorListener);
        }
    };

    private final Runnable mAddActionBarHideOffset = new Runnable() {
        @Override
        public void run() {
            haltActionBarHideOffsetAnimations();
            mCurrentActionBarTopAnimator = mActionBarTop.animate()
                    .translationY(-mActionBarTop.getHeight())
                    .setListener(mTopAnimatorListener);
        }
    };

    static final int[] ATTRS = new int [] {
            R.attr.actionBarSize,
            android.R.attr.windowContentOverlay
    };

    private final NestedScrollingParentHelper mParentHelper;

    public ActionBarOverlayLayout(@NonNull Context context) {
        this(context, null);
    }

    public ActionBarOverlayLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);

        mParentHelper = new NestedScrollingParentHelper(this);
    }

    private void init(Context context) {
        TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS);
        mActionBarHeight = ta.getDimensionPixelSize(0, 0);
        mWindowContentOverlay = ta.getDrawable(1);
        setWillNotDraw(mWindowContentOverlay == null);
        ta.recycle();

        mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
                Build.VERSION_CODES.KITKAT;

        mFlingEstimator = new OverScroller(context);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        haltActionBarHideOffsetAnimations();
    }

    public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) {
        mActionBarVisibilityCallback = cb;
        if (getWindowToken() != null) {
            // This is being initialized after being added to a window;
            // make sure to update all state now.
            mActionBarVisibilityCallback.onWindowVisibilityChanged(mWindowVisibility);
            if (mLastSystemUiVisibility != 0) {
                int newVis = mLastSystemUiVisibility;
                onWindowSystemUiVisibilityChanged(newVis);
                ViewCompat.requestApplyInsets(this);
            }
        }
    }

    public void setOverlayMode(boolean overlayMode) {
        mOverlayMode = overlayMode;

        /*
         * Drawing the window content overlay was broken before K so starting to draw it
         * again unexpectedly will cause artifacts in some apps. They should fix it.
         */
        mIgnoreWindowContentOverlay = overlayMode &&
                getContext().getApplicationInfo().targetSdkVersion <
                        Build.VERSION_CODES.KITKAT;
    }

    public boolean isInOverlayMode() {
        return mOverlayMode;
    }

    public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs) {
        mHasNonEmbeddedTabs = hasNonEmbeddedTabs;
    }

    public void setShowingForActionMode(boolean showing) {
        // TODO: Add workaround for this
//        if (showing) {
//            // Here's a fun hack: if the status bar is currently being hidden,
//            // and the application has asked for stable content insets, then
//            // we will end up with the action mode action bar being shown
//            // without the status bar, but moved below where the status bar
//            // would be.  Not nice.  Trying to have this be positioned
//            // correctly is not easy (basically we need yet *another* content
//            // inset from the window manager to know where to put it), so
//            // instead we will just temporarily force the status bar to be shown.
//            if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
//                    | SYSTEM_UI_FLAG_LAYOUT_STABLE))
//                    == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) {
//                setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
//            }
//        } else {
//            setDisabledSystemUiVisibility(0);
//        }
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        init(getContext());
        ViewCompat.requestApplyInsets(this);
    }

    /**
     * @deprecated see {@link View#onWindowSystemUiVisibilityChanged(int)}
     */
    @Deprecated
    @Override
    @SuppressWarnings("deprecation") /* SYSTEM_UI_FLAG_LAYOUT_* */
    public void onWindowSystemUiVisibilityChanged(int visible) {
        if (Build.VERSION.SDK_INT >= 16) {
            super.onWindowSystemUiVisibilityChanged(visible);
        }
        pullChildren();
        final int diff = mLastSystemUiVisibility ^ visible;
        mLastSystemUiVisibility = visible;
        final boolean barVisible = (visible & SYSTEM_UI_FLAG_FULLSCREEN) == 0;
        final boolean stable = (visible & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
        if (mActionBarVisibilityCallback != null) {
            // We want the bar to be visible if it is not being hidden,
            // or the app has not turned on a stable UI mode (meaning they
            // are performing explicit layout around the action bar).
            mActionBarVisibilityCallback.enableContentAnimations(!stable);
            if (barVisible || !stable) mActionBarVisibilityCallback.showForSystem();
            else mActionBarVisibilityCallback.hideForSystem();
        }
        if ((diff & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
            if (mActionBarVisibilityCallback != null) {
                ViewCompat.requestApplyInsets(this);
            }
        }
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mWindowVisibility = visibility;
        if (mActionBarVisibilityCallback != null) {
            mActionBarVisibilityCallback.onWindowVisibilityChanged(visibility);
        }
    }

    private boolean applyInsets(@NonNull View view, @NonNull Rect insets,
            boolean left, boolean top, boolean bottom, boolean right) {
        boolean changed = false;
        LayoutParams lp = (LayoutParams)view.getLayoutParams();
        if (left && lp.leftMargin != insets.left) {
            changed = true;
            lp.leftMargin = insets.left;
        }
        if (top && lp.topMargin != insets.top) {
            changed = true;
            lp.topMargin = insets.top;
        }
        if (right && lp.rightMargin != insets.right) {
            changed = true;
            lp.rightMargin = insets.right;
        }
        if (bottom && lp.bottomMargin != insets.bottom) {
            changed = true;
            lp.bottomMargin = insets.bottom;
        }
        return changed;
    }

    @SuppressWarnings("deprecation")
    @Override
    protected boolean fitSystemWindows(final Rect insets) {
        if (Build.VERSION.SDK_INT >= 21) {
            // For API 21+, we rely on the WindowInsets path, so delegate to super
            return super.fitSystemWindows(insets);
        }

        pullChildren();

        // The top action bar is always within the content area.
        boolean changed = applyInsets(mActionBarTop, insets,
                true, true, false, true);

        mBaseInnerInsetsRect.set(insets);
        ViewUtils.computeFitSystemWindows(this, mBaseInnerInsetsRect, mBaseContentInsets);
        if (!mLastBaseInnerInsetsRect.equals(mBaseInnerInsetsRect)) {
            changed = true;
            mLastBaseInnerInsetsRect.set(mBaseInnerInsetsRect);
        }
        if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
            changed = true;
            mLastBaseContentInsets.set(mBaseContentInsets);
        }

        if (changed) {
            requestLayout();
        }

        // We don't do any more at this point.  To correctly compute the content/inner
        // insets in all cases, we need to know the measured size of the various action
        // bar elements. fitSystemWindows() happens before the measure pass, so we can't
        // do that here. Instead we will take this up in onMeasure().
        return true;
    }

    @RequiresApi(21)
    @Override
    public WindowInsets onApplyWindowInsets(@NonNull final WindowInsets in) {
        pullChildren();

        final WindowInsetsCompat insets = WindowInsetsCompat.toWindowInsetsCompat(in, this);

        final Rect systemInsets = new Rect(insets.getSystemWindowInsetLeft(),
                insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(),
                insets.getSystemWindowInsetBottom());

        // The top and bottom action bars are always within the content area.
        boolean changed = applyInsets(mActionBarTop, systemInsets, true, true, false, true);

        // Cannot use the result of computeSystemWindowInsets, because that consumes the
        // systemWindowInsets. Instead, we do the insetting by the local insets ourselves.
        ViewCompat.computeSystemWindowInsets(this, insets, mBaseContentInsets);
        mBaseInnerInsets = insets.inset(mBaseContentInsets.left, mBaseContentInsets.top,
                mBaseContentInsets.right, mBaseContentInsets.bottom);

        if (!mLastBaseInnerInsets.equals(mBaseInnerInsets)) {
            changed = true;
            mLastBaseInnerInsets = mBaseInnerInsets;
        }
        if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
            changed = true;
            mLastBaseContentInsets.set(mBaseContentInsets);
        }

        if (changed) {
            requestLayout();
        }

        // We don't do any more at this point.  To correctly compute the content/inner
        // insets in all cases, we need to know the measured size of the various action
        // bar elements. onApplyWindowInsets() happens before the measure pass, so we can't
        // do that here. Instead we will take this up in onMeasure().
        return insets.consumeDisplayCutout()
                .consumeSystemWindowInsets()
                .consumeStableInsets()
                .toWindowInsets();
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    @Override
    @SuppressWarnings("deprecation") /* SYSTEM_UI_FLAG_LAYOUT_* */
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        pullChildren();

        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        int topInset = 0;
        int bottomInset = 0;

        measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0);
        LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams();
        maxWidth = Math.max(maxWidth,
                mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
        maxHeight = Math.max(maxHeight,
                mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
        childState = View.combineMeasuredStates(childState, mActionBarTop.getMeasuredState());

        final int vis = ViewCompat.getWindowSystemUiVisibility(this);
        final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;

        if (stable) {
            // This is the standard space needed for the action bar.  For stable measurement,
            // we can't depend on the size currently reported by it -- this must remain constant.
            topInset = mActionBarHeight;
            if (mHasNonEmbeddedTabs) {
                final View tabs = mActionBarTop.getTabContainer();
                if (tabs != null) {
                    // If tabs are not embedded, increase space on top to account for them.
                    topInset += mActionBarHeight;
                }
            }
        } else if (mActionBarTop.getVisibility() != GONE) {
            // This is the space needed on top of the window for all of the action bar
            // and tabs.
            topInset = mActionBarTop.getMeasuredHeight();
        }

        // If the window has not requested system UI layout flags, we need to
        // make sure its content is not being covered by system UI...  though it
        // will still be covered by the action bar if they have requested it to
        // overlay.
        mContentInsets.set(mBaseContentInsets);
        if (Build.VERSION.SDK_INT >= 21) {
            mInnerInsets = mBaseInnerInsets;
        } else {
            mInnerInsetsRect.set(mBaseInnerInsetsRect);
        }

        if (!mOverlayMode && !stable) {
            mContentInsets.top += topInset;
            mContentInsets.bottom += bottomInset;

            if (Build.VERSION.SDK_INT >= 21) {
                // Content view has been shrunk, shrink all insets to match.
                mInnerInsets = mInnerInsets.inset(0, topInset, 0, bottomInset);
            }
        } else {
            if (Build.VERSION.SDK_INT >= 21) {
                // Add ActionBar to system window inset, but leave other insets untouched.
                Insets sysWindow = Insets.of(
                        mInnerInsets.getSystemWindowInsetLeft(),
                        mInnerInsets.getSystemWindowInsetTop() + topInset,
                        mInnerInsets.getSystemWindowInsetRight(),
                        mInnerInsets.getSystemWindowInsetBottom() + bottomInset
                );
                mInnerInsets = new WindowInsetsCompat.Builder(mInnerInsets)
                        .setSystemWindowInsets(sysWindow)
                        .build();
            } else {
                mInnerInsetsRect.top += topInset;
                mInnerInsetsRect.bottom += bottomInset;
            }
        }
        applyInsets(mContent, mContentInsets, true, true, true, true);

        // If the inner insets have changed, we need to dispatch this down to
        // the app's onApplyWindowInsets(). We do this before measuring the content
        // view to keep the same semantics as the normal fitSystemWindows() call.
        if (Build.VERSION.SDK_INT >= 21 && !mLastInnerInsets.equals(mInnerInsets)) {
            mLastInnerInsets = mInnerInsets;
            ViewCompat.dispatchApplyWindowInsets(mContent, mInnerInsets);
        } else if (Build.VERSION.SDK_INT < 21 && !mLastInnerInsetsRect.equals(mInnerInsetsRect)) {
            mLastInnerInsetsRect.set(mInnerInsetsRect);
            mContent.dispatchFitSystemWindows(mInnerInsetsRect);
        }

        measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
        lp = (LayoutParams) mContent.getLayoutParams();
        maxWidth = Math.max(maxWidth,
                mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
        maxHeight = Math.max(maxHeight,
                mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
        childState = View.combineMeasuredStates(childState, mContent.getMeasuredState());

        // Account for padding too
        maxWidth += getPaddingLeft() + getPaddingRight();
        maxHeight += getPaddingTop() + getPaddingBottom();

        // Check against our minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        setMeasuredDimension(
                View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                View.resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        final int count = getChildCount();

        final int parentLeft = getPaddingLeft();
        final int parentTop = getPaddingTop();

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                final int width = child.getMeasuredWidth();
                final int height = child.getMeasuredHeight();

                int childLeft = parentLeft + lp.leftMargin;
                int childTop = parentTop + lp.topMargin;

                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }

    @Override
    public void draw(Canvas c) {
        super.draw(c);
        if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) {
            final int top = mActionBarTop.getVisibility() == VISIBLE ?
                    (int) (mActionBarTop.getBottom() + mActionBarTop.getTranslationY() + 0.5f)
                    : 0;
            mWindowContentOverlay.setBounds(0, top, getWidth(),
                    top + mWindowContentOverlay.getIntrinsicHeight());
            mWindowContentOverlay.draw(c);
        }
    }

    @Override
    public boolean shouldDelayChildPressedState() {
        return false;
    }

    // NestedScrollingParent3 implementation.

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
            int dyUnconsumed, int type, int[] consumed) {
        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
    }

    // NestedScrollingParent2 implementation.

    @Override
    public boolean onStartNestedScroll(View child, View target, int axes, int type) {
        return type == ViewCompat.TYPE_TOUCH && onStartNestedScroll(child, target, axes);
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes, int type) {
        if (type == ViewCompat.TYPE_TOUCH) {
            onNestedScrollAccepted(child, target, axes);
        }
    }

    @Override
    public void onStopNestedScroll(View target, int type) {
        if (type == ViewCompat.TYPE_TOUCH) {
            onStopNestedScroll(target);
        }
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
            int dyUnconsumed, int type) {
        if (type == ViewCompat.TYPE_TOUCH) {
            onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        }
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int type) {
        if (type == ViewCompat.TYPE_TOUCH) {
            onNestedPreScroll(target, dx, dy, consumed);
        }
    }

    // NestedScrollingParent implementation.

    @Override
    public boolean onStartNestedScroll(View child, View target, int axes) {
        if ((axes & SCROLL_AXIS_VERTICAL) == 0 || mActionBarTop.getVisibility() != VISIBLE) {
            return false;
        }
        return mHideOnContentScroll;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        mParentHelper.onNestedScrollAccepted(child, target, axes);
        mHideOnContentScrollReference = getActionBarHideOffset();
        haltActionBarHideOffsetAnimations();
        if (mActionBarVisibilityCallback != null) {
            mActionBarVisibilityCallback.onContentScrollStarted();
        }
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed) {
        mHideOnContentScrollReference += dyConsumed;
        setActionBarHideOffset(mHideOnContentScrollReference);
    }

    @Override
    public void onStopNestedScroll(View target) {
        if (mHideOnContentScroll && !mAnimatingForFling) {
            if (mHideOnContentScrollReference <= mActionBarTop.getHeight()) {
                postRemoveActionBarHideOffset();
            } else {
                postAddActionBarHideOffset();
            }
        }
        if (mActionBarVisibilityCallback != null) {
            mActionBarVisibilityCallback.onContentScrollStopped();
        }
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        if (!mHideOnContentScroll || !consumed) {
            return false;
        }
        if (shouldHideActionBarOnFling(velocityY)) {
            addActionBarHideOffset();
        } else {
            removeActionBarHideOffset();
        }
        mAnimatingForFling = true;
        return true;
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        // no-op
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        return false;
    }

    @Override
    public int getNestedScrollAxes() {
        return mParentHelper.getNestedScrollAxes();
    }

    void pullChildren() {
        if (mContent == null) {
            mContent = findViewById(R.id.action_bar_activity_content);
            mActionBarTop = findViewById(R.id.action_bar_container);
            mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar));
        }
    }

    private DecorToolbar getDecorToolbar(View view) {
        if (view instanceof DecorToolbar) {
            return (DecorToolbar) view;
        } else if (view instanceof Toolbar) {
            return ((Toolbar) view).getWrapper();
        } else {
            throw new IllegalStateException("Can't make a decor toolbar out of " +
                    view.getClass().getSimpleName());
        }
    }

    public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
        if (hideOnContentScroll != mHideOnContentScroll) {
            mHideOnContentScroll = hideOnContentScroll;
            if (!hideOnContentScroll) {
                haltActionBarHideOffsetAnimations();
                setActionBarHideOffset(0);
            }
        }
    }

    public boolean isHideOnContentScrollEnabled() {
        return mHideOnContentScroll;
    }

    public int getActionBarHideOffset() {
        return mActionBarTop != null ? -((int) mActionBarTop.getTranslationY()) : 0;
    }

    public void setActionBarHideOffset(int offset) {
        haltActionBarHideOffsetAnimations();
        final int topHeight = mActionBarTop.getHeight();
        offset = Math.max(0, Math.min(offset, topHeight));
        mActionBarTop.setTranslationY(-offset);
    }

    void haltActionBarHideOffsetAnimations() {
        removeCallbacks(mRemoveActionBarHideOffset);
        removeCallbacks(mAddActionBarHideOffset);
        if (mCurrentActionBarTopAnimator != null) {
            mCurrentActionBarTopAnimator.cancel();
        }
    }

    private void postRemoveActionBarHideOffset() {
        haltActionBarHideOffsetAnimations();
        postDelayed(mRemoveActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
    }

    private void postAddActionBarHideOffset() {
        haltActionBarHideOffsetAnimations();
        postDelayed(mAddActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
    }

    private void removeActionBarHideOffset() {
        haltActionBarHideOffsetAnimations();
        mRemoveActionBarHideOffset.run();
    }

    private void addActionBarHideOffset() {
        haltActionBarHideOffsetAnimations();
        mAddActionBarHideOffset.run();
    }

    private boolean shouldHideActionBarOnFling(float velocityY) {
        mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
        final int finalY = mFlingEstimator.getFinalY();
        return finalY > mActionBarTop.getHeight();
    }

    @Override
    public void setWindowCallback(Window.Callback cb) {
        pullChildren();
        mDecorToolbar.setWindowCallback(cb);
    }

    @Override
    public void setWindowTitle(CharSequence title) {
        pullChildren();
        mDecorToolbar.setWindowTitle(title);
    }

    @Override
    public CharSequence getTitle() {
        pullChildren();
        return mDecorToolbar.getTitle();
    }

    @Override
    public void initFeature(int windowFeature) {
        pullChildren();
        switch (windowFeature) {
            case Window.FEATURE_PROGRESS:
                mDecorToolbar.initProgress();
                break;
            case Window.FEATURE_INDETERMINATE_PROGRESS:
                mDecorToolbar.initIndeterminateProgress();
                break;
            case AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
                setOverlayMode(true);
                break;
        }
    }

    @Override
    public void setUiOptions(int uiOptions) {
        // Split Action Bar not included.
    }

    @Override
    public boolean hasIcon() {
        pullChildren();
        return mDecorToolbar.hasIcon();
    }

    @Override
    public boolean hasLogo() {
        pullChildren();
        return mDecorToolbar.hasLogo();
    }

    @Override
    public void setIcon(int resId) {
        pullChildren();
        mDecorToolbar.setIcon(resId);
    }

    @Override
    public void setIcon(Drawable d) {
        pullChildren();
        mDecorToolbar.setIcon(d);
    }

    @Override
    public void setLogo(int resId) {
        pullChildren();
        mDecorToolbar.setLogo(resId);
    }

    @Override
    public boolean canShowOverflowMenu() {
        pullChildren();
        return mDecorToolbar.canShowOverflowMenu();
    }

    @Override
    public boolean isOverflowMenuShowing() {
        pullChildren();
        return mDecorToolbar.isOverflowMenuShowing();
    }

    @Override
    public boolean isOverflowMenuShowPending() {
        pullChildren();
        return mDecorToolbar.isOverflowMenuShowPending();
    }

    @Override
    public boolean showOverflowMenu() {
        pullChildren();
        return mDecorToolbar.showOverflowMenu();
    }

    @Override
    public boolean hideOverflowMenu() {
        pullChildren();
        return mDecorToolbar.hideOverflowMenu();
    }

    @Override
    public void setMenuPrepared() {
        pullChildren();
        mDecorToolbar.setMenuPrepared();
    }

    @Override
    public void setMenu(Menu menu, MenuPresenter.Callback cb) {
        pullChildren();
        mDecorToolbar.setMenu(menu, cb);
    }

    @Override
    public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
        pullChildren();
        mDecorToolbar.saveHierarchyState(toolbarStates);
    }

    @Override
    public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
        pullChildren();
        mDecorToolbar.restoreHierarchyState(toolbarStates);
    }

    @Override
    public void dismissPopups() {
        pullChildren();
        mDecorToolbar.dismissPopupMenus();
    }

    public static class LayoutParams extends MarginLayoutParams {
        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(ViewGroup.MarginLayoutParams source) {
            super(source);
        }
    }

    public interface ActionBarVisibilityCallback {
        void onWindowVisibilityChanged(int visibility);
        void showForSystem();
        void hideForSystem();
        void enableContentAnimations(boolean enable);
        void onContentScrollStarted();
        void onContentScrollStopped();
    }
}