1a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/*
2a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Copyright (C) 2015 The Android Open Source Project
3a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
4a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * you may not use this file except in compliance with the License.
6a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * You may obtain a copy of the License at
7a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
8a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
10a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Unless required by applicable law or agreed to in writing, software
11a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * See the License for the specific language governing permissions and
14a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * limitations under the License.
15a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
16a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
17a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespackage android.support.design.widget;
18a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20ee1b969af871a7015fc2df11f8d342a4e6cf40f0Hyundo Moonimport static android.support.v4.utils.ObjectUtils.objectEquals;
218e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
22ae7189188acb2bc972b08d9fbd59939f341c7829Aurimas Liutikasimport android.animation.ValueAnimator;
23a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.Context;
24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.res.TypedArray;
2500a00a7d3ba8279294f63994473afc32e05dcf10Chris Banesimport android.graphics.Rect;
261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banesimport android.os.Build;
27e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcel;
28e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcelable;
2950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef;
3072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.NonNull;
3172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.Nullable;
328f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.support.annotation.RequiresApi;
33c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
3413633353237d6506f4c436b459bc8be8c3d7ed68Chris Banesimport android.support.annotation.VisibleForTesting;
35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R;
3612e0a308d1ab838cb05c395baa6f1251fcbb2363Aurimas Liutikasimport android.support.v4.math.MathUtils;
37d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Roskyimport android.support.v4.os.BuildCompat;
3805f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.view.AbsSavedState;
39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat;
406ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat;
41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet;
42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View;
43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup;
44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator;
45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout;
46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
4750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention;
4850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy;
49d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport java.lang.ref.WeakReference;
50631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList;
51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/**
54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
559fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures.
56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through
58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}.
60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work.
65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
669fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll.
679fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you
689fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}.
699fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available.
70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre>
72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;android.support.design.widget.CoordinatorLayout
73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:android=&quot;http://schemas.android.com/apk/res/android";
74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:app=&quot;http://schemas.android.com/apk/res-auto";
75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_width=&quot;match_parent&quot;
76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_height=&quot;match_parent&quot;&gt;
77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.v4.widget.NestedScrollView
79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;
80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;match_parent&quot;
81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;!-- Your scrolling content --&gt;
84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.v4.widget.NestedScrollView&gt;
86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.design.widget.AppBarLayout
88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;wrap_content&quot;
89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;&gt;
90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.v7.widget.Toolbar
92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.design.widget.TabLayout
96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.design.widget.AppBarLayout&gt;
100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;/android.support.design.widget.CoordinatorLayout&gt;
102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre>
103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar">
105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a>
106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout {
109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
110657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    static final int PENDING_ACTION_NONE = 0x0;
111657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    static final int PENDING_ACTION_EXPANDED = 0x1;
112657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    static final int PENDING_ACTION_COLLAPSED = 0x2;
113657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
1142bab57ed97f284a8152d180cca796739afa3a71dChris Banes    static final int PENDING_ACTION_FORCE = 0x8;
1159995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
117631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
118631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * offset changes.
119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
120631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public interface OnOffsetChangedListener {
121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * child views to implement custom behavior based on the offset (for instance pinning a
124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * view at a certain y value).
125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
126631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes         * @param appBarLayout the {@link AppBarLayout} which offset has changed
12750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes         * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
129631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);
130a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private static final int INVALID_SCROLL_RANGE = -1;
133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mTotalScrollRange = INVALID_SCROLL_RANGE;
135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownScrollRange = INVALID_SCROLL_RANGE;
137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1381a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private boolean mHaveChildWithInterpolator;
13950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private int mPendingAction = PENDING_ACTION_NONE;
1419995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
1426ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    private WindowInsetsCompat mLastInsets;
1436ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
1441a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private List<OnOffsetChangedListener> mListeners;
1451a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1461a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private boolean mCollapsible;
1471a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private boolean mCollapsed;
1481a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
149ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov    private int[] mTmpStatesArray;
150631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context) {
152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        this(context, null);
153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context, AttributeSet attrs) {
156a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super(context, attrs);
157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        setOrientation(VERTICAL);
15850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
159809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes        ThemeUtils.checkAppCompatTheme(context);
160809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes
1611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (Build.VERSION.SDK_INT >= 21) {
1621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // Use the bounds view outline provider so that we cast a shadow, even without a
1631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // background
1641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setBoundsViewOutlineProvider(this);
1651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // If we're running on API 21+, we should reset any state list animator from our
1671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // default style
1681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setStateListAnimatorFromAttrs(this, attrs, 0,
1691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    R.style.Widget_Design_AppBarLayout);
1701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
1711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1721a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
17350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                0, R.style.Widget_Design_AppBarLayout);
17447082c30c630c34829439a9eecd1cf7e8d255a86Aurimas Liutikas        ViewCompat.setBackground(this, a.getDrawable(R.styleable.AppBarLayout_android_background));
1759995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
1762bab57ed97f284a8152d180cca796739afa3a71dChris Banes            setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false), false, false);
1779995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        }
1781a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (Build.VERSION.SDK_INT >= 21 && a.hasValue(R.styleable.AppBarLayout_elevation)) {
1791a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(
1801a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0));
1811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
182d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky        if (BuildCompat.isAtLeastO()) {
183d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky            // In O+, we have these values set in the style. Since there is no defStyleAttr for
184d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky            // AppBarLayout at the AppCompat level, check for these attributes here.
185d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky            if (a.hasValue(R.styleable.AppBarLayout_android_keyboardNavigationCluster)) {
186d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky                this.setKeyboardNavigationCluster(a.getBoolean(
187d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky                        R.styleable.AppBarLayout_android_keyboardNavigationCluster, false));
188d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky            }
189d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky            if (a.hasValue(R.styleable.AppBarLayout_android_touchscreenBlocksFocus)) {
190d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky                this.setTouchscreenBlocksFocus(a.getBoolean(
191d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky                        R.styleable.AppBarLayout_android_touchscreenBlocksFocus, false));
192d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky            }
193d2d90794ffd1ab661708ec28c1f5e000c813aaf8Evan Rosky        }
19450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        a.recycle();
19550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1966ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        ViewCompat.setOnApplyWindowInsetsListener(this,
1976ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                new android.support.v4.view.OnApplyWindowInsetsListener() {
1986ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    @Override
1996ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    public WindowInsetsCompat onApplyWindowInsets(View v,
2006ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                            WindowInsetsCompat insets) {
2018818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes                        return onWindowInsetChanged(insets);
2026ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    }
2036ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                });
204631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
205631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
206631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
207631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Add a listener that will be called when the offset of this {@link AppBarLayout} changes.
208631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
209631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener The listener that will be called when the offset changes.]
210631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
211631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see #removeOnOffsetChangedListener(OnOffsetChangedListener)
212631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
213631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void addOnOffsetChangedListener(OnOffsetChangedListener listener) {
2141a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mListeners == null) {
2151a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            mListeners = new ArrayList<>();
2161a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
2176f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes        if (listener != null && !mListeners.contains(listener)) {
2186f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes            mListeners.add(listener);
219631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
220631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
221631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
222631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
223631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Remove the previously added {@link OnOffsetChangedListener}.
224631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
225631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener the listener to remove.
226631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
227631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
2281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mListeners != null && listener != null) {
2296f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes            mListeners.remove(listener);
230631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
234ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
235ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
236ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        invalidateScrollRanges();
237ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
238ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
239ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    @Override
240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected void onLayout(boolean changed, int l, int t, int r, int b) {
241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.onLayout(changed, l, t, r, b);
24279aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes        invalidateScrollRanges();
243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mHaveChildWithInterpolator = false;
245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final Interpolator interpolator = childLp.getScrollInterpolator();
249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (interpolator != null) {
251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mHaveChildWithInterpolator = true;
252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
2551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
2561a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        updateCollapsible();
2571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
2581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
2591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private void updateCollapsible() {
2601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        boolean haveCollapsibleChild = false;
2611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
2621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            if (((LayoutParams) getChildAt(i).getLayoutParams()).isCollapsible()) {
2631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                haveCollapsibleChild = true;
2641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                break;
2651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
2661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
26757aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes        setCollapsibleState(haveCollapsibleChild);
268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
270ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private void invalidateScrollRanges() {
271ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        // Invalidate the scroll ranges
272ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mTotalScrollRange = INVALID_SCROLL_RANGE;
273ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
274ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mDownScrollRange = INVALID_SCROLL_RANGE;
275ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
276ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public void setOrientation(int orientation) {
279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (orientation != VERTICAL) {
280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            throw new IllegalArgumentException("AppBarLayout is always vertical and does"
281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    + " not support horizontal orientation");
282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.setOrientation(orientation);
284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
2869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    /**
2879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already
2889995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * been laid out.
2899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2909995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
2919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * direct child of a {@link CoordinatorLayout}.</p>
2929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2939995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param expanded true if the layout should be fully expanded, false if it should
2949995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *                 be fully collapsed
2959995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2969995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
2979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     */
2989995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    public void setExpanded(boolean expanded) {
2999995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        setExpanded(expanded, ViewCompat.isLaidOut(this));
3009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
3019995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
3029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    /**
3039995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * Sets whether this {@link AppBarLayout} is expanded or not.
3049995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
3059995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
3069995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * direct child of a {@link CoordinatorLayout}.</p>
3079995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
3089995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param expanded true if the layout should be fully expanded, false if it should
3099995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *                 be fully collapsed
3109995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param animate Whether to animate to the new state
3119995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
3129995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
3139995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     */
3149995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    public void setExpanded(boolean expanded, boolean animate) {
3152bab57ed97f284a8152d180cca796739afa3a71dChris Banes        setExpanded(expanded, animate, true);
3162bab57ed97f284a8152d180cca796739afa3a71dChris Banes    }
3172bab57ed97f284a8152d180cca796739afa3a71dChris Banes
3182bab57ed97f284a8152d180cca796739afa3a71dChris Banes    private void setExpanded(boolean expanded, boolean animate, boolean force) {
3199995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED)
3202bab57ed97f284a8152d180cca796739afa3a71dChris Banes                | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0)
3212bab57ed97f284a8152d180cca796739afa3a71dChris Banes                | (force ? PENDING_ACTION_FORCE : 0);
3229995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        requestLayout();
3239995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
3249995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return p instanceof LayoutParams;
328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateDefaultLayoutParams() {
332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public LayoutParams generateLayoutParams(AttributeSet attrs) {
337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(getContext(), attrs);
338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3428f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette        if (Build.VERSION.SDK_INT >= 19 && p instanceof LinearLayout.LayoutParams) {
343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((LinearLayout.LayoutParams) p);
344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        } else if (p instanceof MarginLayoutParams) {
345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((MarginLayoutParams) p);
346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(p);
348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
350657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    boolean hasChildWithInterpolator() {
351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mHaveChildWithInterpolator;
352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
3556ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes     * Returns the scroll range of all children.
356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return the scroll range in px
358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
3596ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    public final int getTotalScrollRange() {
360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mTotalScrollRange != INVALID_SCROLL_RANGE) {
361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mTotalScrollRange;
362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
368ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            final int childHeight = child.getMeasuredHeight();
369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
373b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                range += childHeight + lp.topMargin + lp.bottomMargin;
374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing scroll, we to take the collapsed height into account.
3776ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    // We also break straight away since later views can't scroll beneath
378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // us
3796ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    range -= ViewCompat.getMinimumHeight(child);
3806ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    break;
381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
3884832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes        return mTotalScrollRange = Math.max(0, range - getTopInset());
389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
391657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    boolean hasScrollableChildren() {
392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return getTotalScrollRange() != 0;
393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling up from a nested pre-scroll.
397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
398657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    int getUpNestedPreScrollRange() {
39950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return getTotalScrollRange();
400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested pre-scroll.
404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
405657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    int getDownNestedPreScrollRange() {
406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownPreScrollRange;
409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
415ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            final int childHeight = child.getMeasuredHeight();
416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
419b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                // First take the margin into account
420b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                range += lp.topMargin + lp.bottomMargin;
421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // The view has the quick return flag combination...
422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) {
423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If they're set to enter collapsed, use the minimum height
424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += ViewCompat.getMinimumHeight(child);
42547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
42647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    // Only enter by the amount of the collapsed height
42747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    range += childHeight - ViewCompat.getMinimumHeight(child);
428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
429116332176fa904af2f701011de8d4f2524c10a8eChris Banes                    // Else use the full height (minus the top inset)
430116332176fa904af2f701011de8d4f2524c10a8eChris Banes                    range += childHeight - getTopInset();
431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (range > 0) {
433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If we've hit an non-quick return scrollable view, and we've already hit a
434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // quick return view, return now
435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
43813633353237d6506f4c436b459bc8be8c3d7ed68Chris Banes        return mDownPreScrollRange = Math.max(0, range);
439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested scroll.
443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
444657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    int getDownNestedScrollRange() {
445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownScrollRange != INVALID_SCROLL_RANGE) {
446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownScrollRange;
448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
451c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
454ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            int childHeight = child.getMeasuredHeight();
455b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes            childHeight += lp.topMargin + lp.bottomMargin;
456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing exit scroll, we to take the collapsed height into account.
4654832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    // We also break the range straight away since later views can't scroll
466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // beneath us
4674832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    range -= ViewCompat.getMinimumHeight(child) + getTopInset();
4684832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    break;
469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
4764c33be829067714342b629d29329206bc2116afeChris Banes        return mDownScrollRange = Math.max(0, range);
477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
479657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    void dispatchOffsetUpdates(int offset) {
4801a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        // Iterate backwards through the list so that most recently added listeners
4811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        // get the first chance to decide
4821a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mListeners != null) {
4831a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            for (int i = 0, z = mListeners.size(); i < z; i++) {
4841a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final OnOffsetChangedListener listener = mListeners.get(i);
4851a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                if (listener != null) {
4861a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    listener.onOffsetChanged(this, offset);
4871a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                }
4881a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
4891a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
4901a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
4911a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
49250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    final int getMinimumHeightForVisibleOverlappingContent() {
49317ed3263761329f6aa6796941358c41001fff325Chris Banes        final int topInset = getTopInset();
49450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int minHeight = ViewCompat.getMinimumHeight(this);
49550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        if (minHeight != 0) {
49650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // If this layout has a min height, use it (doubled)
4976ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            return (minHeight * 2) + topInset;
49850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
49950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
50050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Otherwise, we'll use twice the min height of our last child
50150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int childCount = getChildCount();
502b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        final int lastChildMinHeight = childCount >= 1
503b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes                ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) : 0;
504b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        if (lastChildMinHeight != 0) {
505b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes            return (lastChildMinHeight * 2) + topInset;
506b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        }
507b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes
508b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        // If we reach here then we don't have a min height explicitly set. Instead we'll take a
509b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        // guess at 1/3 of our height being visible
510b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        return getHeight() / 3;
51150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
51250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
5131a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    @Override
5141a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    protected int[] onCreateDrawableState(int extraSpace) {
515ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov        if (mTmpStatesArray == null) {
516ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov            // Note that we can't allocate this at the class level (in declaration) since
517ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov            // some paths in super View constructor are going to call this method before
518ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov            // that
519ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov            mTmpStatesArray = new int[2];
520ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov        }
5211a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        final int[] extraStates = mTmpStatesArray;
5221a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        final int[] states = super.onCreateDrawableState(extraSpace + extraStates.length);
5231a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
5241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        extraStates[0] = mCollapsible ? R.attr.state_collapsible : -R.attr.state_collapsible;
5251a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        extraStates[1] = mCollapsible && mCollapsed
5261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                ? R.attr.state_collapsed : -R.attr.state_collapsed;
5271a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
5281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        return mergeDrawableStates(states, extraStates);
5291a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
5301a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
531c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes    /**
532c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     * Sets whether the AppBarLayout has collapsible children or not.
533c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     *
534c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     * @return true if the collapsible state changed
535c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     */
536c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes    private boolean setCollapsibleState(boolean collapsible) {
5371a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mCollapsible != collapsible) {
5381a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            mCollapsible = collapsible;
5391a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            refreshDrawableState();
540c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            return true;
5411a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
542c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes        return false;
5431a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
5441a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
545c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes    /**
546c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     * Sets whether the AppBarLayout is in a collapsed state or not.
547c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     *
548c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     * @return true if the collapsed state changed
549c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes     */
550657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    boolean setCollapsedState(boolean collapsed) {
5511a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mCollapsed != collapsed) {
5521a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            mCollapsed = collapsed;
5531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            refreshDrawableState();
554c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            return true;
5551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
556c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes        return false;
5571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
5581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
55950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    /**
5601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes     * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
561926cd28257536b1206934d69585a2dab100147d5Chris Banes     * controlled via a {@link android.animation.StateListAnimator}. If a target
562c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas     * elevation is set, either by this method or the {@code app:elevation} attribute,
563926cd28257536b1206934d69585a2dab100147d5Chris Banes     * a new state list animator is created which uses the given {@code elevation} value.
564631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
5651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_elevation
56650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes     */
5671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    @Deprecated
568631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void setTargetElevation(float elevation) {
5691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (Build.VERSION.SDK_INT >= 21) {
5701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(this, elevation);
5711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
572631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
573631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
574631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
5751a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes     * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
576926cd28257536b1206934d69585a2dab100147d5Chris Banes     * controlled via a {@link android.animation.StateListAnimator}. This method now
577926cd28257536b1206934d69585a2dab100147d5Chris Banes     * always returns 0.
578631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
5791a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    @Deprecated
580631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public float getTargetElevation() {
5811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        return 0;
58250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
58350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
584657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    int getPendingAction() {
5859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        return mPendingAction;
5869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
5879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
588657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    void resetPendingAction() {
5899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        mPendingAction = PENDING_ACTION_NONE;
5909995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
5919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
59213633353237d6506f4c436b459bc8be8c3d7ed68Chris Banes    @VisibleForTesting
59313633353237d6506f4c436b459bc8be8c3d7ed68Chris Banes    final int getTopInset() {
594ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
595ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
596ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
597657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) {
5988818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        WindowInsetsCompat newInsets = null;
5998818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
6008818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        if (ViewCompat.getFitsSystemWindows(this)) {
6018818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            // If we're set to fit system windows, keep the insets
6028818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            newInsets = insets;
6038818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        }
6048818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
6058818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        // If our insets have changed, keep them and invalidate the scroll ranges...
606101d1376f87b5d4e82942a0b264c99a9a009b521Kirill Grouchnikov        if (!objectEquals(mLastInsets, newInsets)) {
6078818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            mLastInsets = newInsets;
6088818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            invalidateScrollRanges();
6098818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        }
6108818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
61117ed3263761329f6aa6796941358c41001fff325Chris Banes        return insets;
6126ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    }
6136ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class LayoutParams extends LinearLayout.LayoutParams {
61550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
61650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        /** @hide */
6178e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas        @RestrictTo(LIBRARY_GROUP)
61850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @IntDef(flag=true, value={
61950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_SCROLL,
62050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
62150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_ENTER_ALWAYS,
622bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED,
623bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                SCROLL_FLAG_SNAP
62450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        })
62550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Retention(RetentionPolicy.SOURCE)
62650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public @interface ScrollFlags {}
62750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * The view will be scroll in direct relation to scroll events. This flag needs to be
630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * set for any of the other flags to take effect. If any sibling views
631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * before this one do not have this flag, then this value has no effect.
632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_SCROLL = 0x1;
634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When exiting (scrolling off screen) the view will be scrolled until it is
637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * 'collapsed'. The collapsed height is defined by the view's minimum height.
638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;
643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When entering (scrolling on screen) the view will scroll on any downwards
646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * scroll event, regardless of whether the scrolling view is also scrolling. This
647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * is commonly referred to as the 'quick return' pattern.
648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
649a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;
650a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
651a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
652a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * An additional flag for 'enterAlways' which modifies the returning view to
653a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * only initially scroll back to it's collapsed height. Once the scrolling view has
654a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * reached the end of it's scroll range, the remainder of this view will be scrolled
655a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * into view. The collapsed height is defined by the view's minimum height.
656a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
657a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
658a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
663bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * Upon a scroll ending, if the view is only partially visible then it will be snapped
664bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * and scrolled to it's closest edge. For example, if the view only has it's bottom 25%
665bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75%
666bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * is visible then it will be scrolled fully into view.
667bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         */
668bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        public static final int SCROLL_FLAG_SNAP = 0x10;
669bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
670bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        /**
671bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * Internal flags which allows quick checking features
672a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
673a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
674bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP;
6751a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        static final int COLLAPSIBLE_FLAGS = SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
6761a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                | SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED;
677a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
678a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int mScrollFlags = SCROLL_FLAG_SCROLL;
679a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        Interpolator mScrollInterpolator;
680a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
681a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(Context c, AttributeSet attrs) {
682a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(c, attrs);
683c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_Layout);
684c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes            mScrollFlags = a.getInt(R.styleable.AppBarLayout_Layout_layout_scrollFlags, 0);
685c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes            if (a.hasValue(R.styleable.AppBarLayout_Layout_layout_scrollInterpolator)) {
686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int resId = a.getResourceId(
687c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes                        R.styleable.AppBarLayout_Layout_layout_scrollInterpolator, 0);
688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(
689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        c, resId);
690a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
691a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
692a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
693a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height) {
695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height);
696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
698a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height, float weight) {
699a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height, weight);
700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(ViewGroup.LayoutParams p) {
703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(p);
704a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(MarginLayoutParams source) {
707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
708a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
7108f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette        @RequiresApi(19)
711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LinearLayout.LayoutParams source) {
7128f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette            // The copy constructor called here only exists on API 19+.
713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
715a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
7168f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette        @RequiresApi(19)
717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LayoutParams source) {
7188f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette            // The copy constructor called here only exists on API 19+.
719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = source.mScrollFlags;
721a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = source.mScrollInterpolator;
722a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
723a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
724a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
725a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the scrolling flags.
726a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
727a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
728bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         *             {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS},
729bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         *             {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED} and {@link #SCROLL_FLAG_SNAP }.
730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollFlags()
732a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
73373e7a06fb66110495fa4e98a91831a47feab2526Chris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollFlags
734a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
73550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public void setScrollFlags(@ScrollFlags int flags) {
736a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = flags;
737a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
738a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
739a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
740a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the scrolling flags.
741a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
742a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollFlags(int)
743a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
74473e7a06fb66110495fa4e98a91831a47feab2526Chris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollFlags
745a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
74650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @ScrollFlags
747a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getScrollFlags() {
748a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollFlags;
749a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
750a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
751a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
752a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the interpolator to when scrolling the view associated with this
753a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}.
754a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
755a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling.
756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
75773e7a06fb66110495fa4e98a91831a47feab2526Chris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollInterpolator
758a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollInterpolator()
759a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
760a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setScrollInterpolator(Interpolator interpolator) {
761a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = interpolator;
762a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
763a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
764a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
765a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the {@link Interpolator} being used for scrolling the view associated with this
766a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling.
767a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
76873e7a06fb66110495fa4e98a91831a47feab2526Chris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollInterpolator
769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollInterpolator(Interpolator)
770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
771a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Interpolator getScrollInterpolator() {
772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollInterpolator;
773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
7741a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
7751a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        /**
7761a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes         * Returns true if the scroll flags are compatible for 'collapsing'
7771a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes         */
778657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas        boolean isCollapsible() {
7791a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            return (mScrollFlags & SCROLL_FLAG_SCROLL) == SCROLL_FLAG_SCROLL
7801a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    && (mScrollFlags & COLLAPSIBLE_FLAGS) != 0;
7811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
782a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
783a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
784a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
785a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested
786a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * scroll handling with offsetting.
787a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
788ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    public static class Behavior extends HeaderBehavior<AppBarLayout> {
78957aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes        private static final int MAX_OFFSET_ANIMATION_DURATION = 600; // ms
790e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private static final int INVALID_POSITION = -1;
791e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
79272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        /**
79372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * Callback to allow control over any {@link AppBarLayout} dragging.
79472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         */
79572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        public static abstract class DragCallback {
79672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            /**
79772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * Allows control over whether the given {@link AppBarLayout} can be dragged or not.
79872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *
79972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * <p>Dragging is defined as a direct touch on the AppBarLayout with movement. This
80072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * call does not affect any nested scrolling.</p>
80172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *
80272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * @return true if we are in a position to scroll the AppBarLayout via a drag, false
80372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *         if not.
80472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             */
80572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            public abstract boolean canDrag(@NonNull AppBarLayout appBarLayout);
80672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
80772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
808e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private int mOffsetDelta;
809ae7189188acb2bc972b08d9fbd59939f341c7829Aurimas Liutikas        private ValueAnimator mOffsetAnimator;
81050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
811e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
812e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
813e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private float mOffsetToChildIndexOnLayoutPerc;
814e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
815d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        private WeakReference<View> mLastNestedScrollingChildRef;
81672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        private DragCallback mOnDragCallback;
817d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
818a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior() {}
819a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
820a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior(Context context, AttributeSet attrs) {
821a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
822a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
823a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
824a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
825ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes        public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
82676daed103193a1756535d1f59b165e98e1d17445Chris Banes                View directTargetChild, View target, int nestedScrollAxes, int type) {
827f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes            // Return true if we're nested scrolling vertically, and we have scrollable children
828f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes            // and the scrolling view is big enough to scroll
829631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
830f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes                    && child.hasScrollableChildren()
831ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes                    && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight();
832631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
833c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            if (started && mOffsetAnimator != null) {
834631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // Cancel any offset animation
835c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator.cancel();
836631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
837631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
838d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            // A new nested scroll has started so clear out the previous ref
839d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            mLastNestedScrollingChildRef = null;
840d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
841631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return started;
842a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
843a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
844a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
845a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
84676daed103193a1756535d1f59b165e98e1d17445Chris Banes                View target, int dx, int dy, int[] consumed, int type) {
84776daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (dy != 0) {
84850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int min, max;
84950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (dy < 0) {
85050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling down
85150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getTotalScrollRange();
85250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = min + child.getDownNestedPreScrollRange();
85350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                } else {
85450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling up
85550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getUpNestedPreScrollRange();
85650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = 0;
85750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                }
85876daed103193a1756535d1f59b165e98e1d17445Chris Banes                if (min != max) {
85976daed103193a1756535d1f59b165e98e1d17445Chris Banes                    consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
86076daed103193a1756535d1f59b165e98e1d17445Chris Banes                }
861a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
862a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
863a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
864a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
865a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
86676daed103193a1756535d1f59b165e98e1d17445Chris Banes                View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
86776daed103193a1756535d1f59b165e98e1d17445Chris Banes                int type) {
86850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dyUnconsumed < 0) {
86950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // If the scrolling view is scrolling down but not consuming, it's probably be at
87050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // the top of it's content
87150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                scroll(coordinatorLayout, child, dyUnconsumed,
87250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        -child.getDownNestedScrollRange(), 0);
873a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
87450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
875a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
87650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Override
877bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,
87876daed103193a1756535d1f59b165e98e1d17445Chris Banes                View target, int type) {
87976daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (type == ViewCompat.TYPE_TOUCH) {
880bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                // If we haven't been flung then let's see if the current view has been set to snap
881bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                snapToChildIfNeeded(coordinatorLayout, abl);
882bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
883bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
884d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            // Keep a reference to the previous nested scrolling child
885d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            mLastNestedScrollingChildRef = new WeakReference<>(target);
886d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        }
887d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
88872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        /**
88972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * Set a callback to control any {@link AppBarLayout} dragging.
89072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         *
89172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * @param callback the callback to use, or {@code null} to use the default behavior.
89272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         */
89372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        public void setDragCallback(@Nullable DragCallback callback) {
89472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mOnDragCallback = callback;
89572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
89672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
897631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
89857aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes                final AppBarLayout child, final int offset, float velocity) {
89957aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            final int distance = Math.abs(getTopBottomOffsetForScrollingSibling() - offset);
90057aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes
90157aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            final int duration;
90257aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            velocity = Math.abs(velocity);
90357aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            if (velocity > 0) {
90457aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes                duration = 3 * Math.round(1000 * (distance / velocity));
90557aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            } else {
90657aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes                final float distanceRatio = (float) distance / child.getHeight();
90757aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes                duration = (int) ((distanceRatio + 1) * 150);
90857aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            }
90957aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes
91057aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            animateOffsetWithDuration(coordinatorLayout, child, offset, duration);
91157aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes        }
91257aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes
91357aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes        private void animateOffsetWithDuration(final CoordinatorLayout coordinatorLayout,
91457aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes                final AppBarLayout child, final int offset, final int duration) {
915dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            final int currentOffset = getTopBottomOffsetForScrollingSibling();
916dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            if (currentOffset == offset) {
917c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) {
918c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                    mOffsetAnimator.cancel();
919dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                }
920dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                return;
921dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            }
922dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
923c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            if (mOffsetAnimator == null) {
924ae7189188acb2bc972b08d9fbd59939f341c7829Aurimas Liutikas                mOffsetAnimator = new ValueAnimator();
925c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
926ae7189188acb2bc972b08d9fbd59939f341c7829Aurimas Liutikas                mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
927631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    @Override
928ae7189188acb2bc972b08d9fbd59939f341c7829Aurimas Liutikas                    public void onAnimationUpdate(ValueAnimator animation) {
929ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(coordinatorLayout, child,
930ae7189188acb2bc972b08d9fbd59939f341c7829Aurimas Liutikas                                (int) animation.getAnimatedValue());
931631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
932631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                });
933631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
934c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator.cancel();
935631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
936631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
93757aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes            mOffsetAnimator.setDuration(Math.min(duration, MAX_OFFSET_ANIMATION_DURATION));
938c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            mOffsetAnimator.setIntValues(currentOffset, offset);
939c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            mOffsetAnimator.start();
940631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
941631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
942108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes        private int getChildIndexOnOffset(AppBarLayout abl, final int offset) {
943bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            for (int i = 0, count = abl.getChildCount(); i < count; i++) {
944bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                View child = abl.getChildAt(i);
945bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                if (child.getTop() <= -offset && child.getBottom() >= -offset) {
946108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    return i;
947bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                }
948bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
949108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes            return -1;
950bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        }
951bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
952bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) {
953bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            final int offset = getTopBottomOffsetForScrollingSibling();
954108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes            final int offsetChildIndex = getChildIndexOnOffset(abl, offset);
955108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes            if (offsetChildIndex >= 0) {
956108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                final View offsetChild = abl.getChildAt(offsetChildIndex);
957bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams();
958108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                final int flags = lp.getScrollFlags();
959108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes
960108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                if ((flags & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) {
961bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    // We're set the snap, so animate the offset to the nearest edge
962108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    int snapTop = -offsetChild.getTop();
963108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    int snapBottom = -offsetChild.getBottom();
964108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes
965108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    if (offsetChildIndex == abl.getChildCount() - 1) {
966108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        // If this is the last child, we need to take the top inset into account
967108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        snapBottom += abl.getTopInset();
968108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    }
969dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
970108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    if (checkFlag(flags, LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED)) {
971108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        // If the view is set only exit until it is collapsed, we'll abide by that
972108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        snapBottom += ViewCompat.getMinimumHeight(offsetChild);
973108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    } else if (checkFlag(flags, LayoutParams.FLAG_QUICK_RETURN
974108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                            | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS)) {
975108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        // If it's set to always enter collapsed, it actually has two states. We
976108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        // select the state and then snap within the state
977108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        final int seam = snapBottom + ViewCompat.getMinimumHeight(offsetChild);
978108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        if (offset < seam) {
979108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                            snapTop = seam;
980108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        } else {
981108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                            snapBottom = seam;
982108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                        }
983dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    }
984dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
985108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                    final int newOffset = offset < (snapBottom + snapTop) / 2
986108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                            ? snapBottom
987108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes                            : snapTop;
988bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    animateOffsetTo(coordinatorLayout, abl,
98912e0a308d1ab838cb05c395baa6f1251fcbb2363Aurimas Liutikas                            MathUtils.clamp(newOffset, -abl.getTotalScrollRange(), 0), 0);
990bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                }
991bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
992bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        }
993bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
994108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes        private static boolean checkFlag(final int flags, final int check) {
995108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes            return (flags & check) == check;
996108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes        }
997108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes
998631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        @Override
999bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes        public boolean onMeasureChild(CoordinatorLayout parent, AppBarLayout child,
1000bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
1001bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                int heightUsed) {
1002bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            final CoordinatorLayout.LayoutParams lp =
1003bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                    (CoordinatorLayout.LayoutParams) child.getLayoutParams();
1004bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            if (lp.height == CoordinatorLayout.LayoutParams.WRAP_CONTENT) {
1005bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // If the view is set to wrap on it's height, CoordinatorLayout by default will
1006bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // cap the view at the CoL's height. Since the AppBarLayout can scroll, this isn't
1007bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // what we actually want, so we measure it ourselves with an unspecified spec to
1008bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // allow the child to be larger than it's parent
1009bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed,
1010bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightUsed);
1011bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                return true;
1012bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            }
1013bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes
1014bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            // Let the parent handle it as normal
1015bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed,
1016bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                    parentHeightMeasureSpec, heightUsed);
1017bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes        }
1018bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes
1019bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes        @Override
10209995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl,
102150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int layoutDirection) {
10229995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
10239995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
10242bab57ed97f284a8152d180cca796739afa3a71dChris Banes            // The priority for for actions here is (first which is true wins):
10252bab57ed97f284a8152d180cca796739afa3a71dChris Banes            // 1. forced pending actions
10262bab57ed97f284a8152d180cca796739afa3a71dChris Banes            // 2. offsets for restorations
10272bab57ed97f284a8152d180cca796739afa3a71dChris Banes            // 3. non-forced pending actions
10289995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            final int pendingAction = abl.getPendingAction();
10292bab57ed97f284a8152d180cca796739afa3a71dChris Banes            if (mOffsetToChildIndexOnLayout >= 0 && (pendingAction & PENDING_ACTION_FORCE) == 0) {
10302bab57ed97f284a8152d180cca796739afa3a71dChris Banes                View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
10312bab57ed97f284a8152d180cca796739afa3a71dChris Banes                int offset = -child.getBottom();
10322bab57ed97f284a8152d180cca796739afa3a71dChris Banes                if (mOffsetToChildIndexOnLayoutIsMinHeight) {
10332bab57ed97f284a8152d180cca796739afa3a71dChris Banes                    offset += ViewCompat.getMinimumHeight(child) + abl.getTopInset();
10342bab57ed97f284a8152d180cca796739afa3a71dChris Banes                } else {
10352bab57ed97f284a8152d180cca796739afa3a71dChris Banes                    offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
10362bab57ed97f284a8152d180cca796739afa3a71dChris Banes                }
10372bab57ed97f284a8152d180cca796739afa3a71dChris Banes                setHeaderTopBottomOffset(parent, abl, offset);
10382bab57ed97f284a8152d180cca796739afa3a71dChris Banes            } else if (pendingAction != PENDING_ACTION_NONE) {
10399995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0;
10409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) {
10419995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    final int offset = -abl.getUpNestedPreScrollRange();
10429995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    if (animate) {
104357aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes                        animateOffsetTo(parent, abl, offset, 0);
10449995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    } else {
1045ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(parent, abl, offset);
10469995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    }
10479995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) {
10489995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    if (animate) {
104957aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes                        animateOffsetTo(parent, abl, 0, 0);
10509995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    } else {
1051ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(parent, abl, 0);
10529995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    }
10539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                }
1054e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
105550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
105679aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            // Finally reset any pending states
105779aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            abl.resetPendingAction();
105879aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            mOffsetToChildIndexOnLayout = INVALID_POSITION;
105979aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes
106047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // We may have changed size, so let's constrain the top and bottom offset correctly,
106147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // just in case we're out of the bounds
106247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            setTopAndBottomOffset(
106312e0a308d1ab838cb05c395baa6f1251fcbb2363Aurimas Liutikas                    MathUtils.clamp(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
106447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes
1065df2d45b76203a9c4101629005eb27059d7f80297Chris Banes            // Update the AppBarLayout's drawable state for any elevation changes.
1066df2d45b76203a9c4101629005eb27059d7f80297Chris Banes            // This is needed so that the elevation is set in the first layout, so that
1067df2d45b76203a9c4101629005eb27059d7f80297Chris Banes            // we don't get a visual elevation jump pre-N (due to the draw dispatch skip)
1068df2d45b76203a9c4101629005eb27059d7f80297Chris Banes            updateAppBarLayoutDrawableState(parent, abl, getTopAndBottomOffset(), 0, true);
1069df2d45b76203a9c4101629005eb27059d7f80297Chris Banes
10701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // Make sure we dispatch the offset update
10711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            abl.dispatchOffsetUpdates(getTopAndBottomOffset());
107250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
107350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            return handled;
1074a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1075a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
107672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
107772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        boolean canDragView(AppBarLayout view) {
107872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            if (mOnDragCallback != null) {
107972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // If there is a drag callback set, it's in control
108072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return mOnDragCallback.canDrag(view);
108172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
108272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
108372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            // Else we'll use the default behaviour of seeing if it can scroll down
1084d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            if (mLastNestedScrollingChildRef != null) {
108572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // If we have a reference to a scrolling view, check it
108672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final View scrollingView = mLastNestedScrollingChildRef.get();
108772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return scrollingView != null && scrollingView.isShown()
108872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                        && !ViewCompat.canScrollVertically(scrollingView, -1);
108972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            } else {
109072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // Otherwise we assume that the scrolling view hasn't been scrolled and can drag.
109172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return true;
1092d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            }
1093d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        }
1094d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
1095ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
109647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        void onFlingFinished(CoordinatorLayout parent, AppBarLayout layout) {
109747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // At the end of a manual fling, check to see if we need to snap to the edge-child
109847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            snapToChildIfNeeded(parent, layout);
109947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        }
110047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes
110147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        @Override
110272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getMaxDragOffset(AppBarLayout view) {
110372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            return -view.getDownNestedScrollRange();
110472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
110572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
110672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
110772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getScrollRangeForDragFling(AppBarLayout view) {
110872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            return view.getTotalScrollRange();
110972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
111072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
111172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
111272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout,
1113bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes                AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) {
1114e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final int curOffset = getTopBottomOffsetForScrollingSibling();
1115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int consumed = 0;
1116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
11175e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
1118631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we have some scrolling range, and we're currently within the min and max
1119631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // offsets, calculate a new offset
112012e0a308d1ab838cb05c395baa6f1251fcbb2363Aurimas Liutikas                newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset);
1121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (curOffset != newOffset) {
1122e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    final int interpolatedOffset = appBarLayout.hasChildWithInterpolator()
1123e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            ? interpolateOffset(appBarLayout, newOffset)
1124e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            : newOffset;
1125e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
11261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    final boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
1127631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
1128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update how much dy we have consumed
1129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    consumed = curOffset - newOffset;
1130a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update the stored sibling offset
1131e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    mOffsetDelta = newOffset - interpolatedOffset;
1132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
1134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the offset hasn't changed and we're using an interpolated scroll
1135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // then we need to keep any dependent views updated. CoL will do this for
1136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // us when we move, but we need to do it manually when we don't (as an
1137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // interpolated scroll may finish early).
1138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
1139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
1140a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1141631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // Dispatch the updates to any listeners
11421a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    appBarLayout.dispatchOffsetUpdates(getTopAndBottomOffset());
11431a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
11441a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    // Update the AppBarLayout's drawable state (for any elevation changes)
1145c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                    updateAppBarLayoutDrawableState(coordinatorLayout, appBarLayout, newOffset,
1146df2d45b76203a9c4101629005eb27059d7f80297Chris Banes                            newOffset < curOffset ? -1 : 1, false);
1147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
11485e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            } else {
11495e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // Reset the offset delta
11505e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                mOffsetDelta = 0;
1151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return consumed;
1154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1156bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes        @VisibleForTesting
1157bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes        boolean isOffsetAnimatorRunning() {
1158bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes            return mOffsetAnimator != null && mOffsetAnimator.isRunning();
1159bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes        }
1160bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes
1161a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int interpolateOffset(AppBarLayout layout, final int offset) {
1162a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int absOffset = Math.abs(offset);
1163a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1164a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
1165a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View child = layout.getChildAt(i);
1166a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
116750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                final Interpolator interpolator = childLp.getScrollInterpolator();
1168a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
116950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
1170a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (interpolator != null) {
117150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        int childScrollableHeight = 0;
117250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        final int flags = childLp.getScrollFlags();
117350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
1174b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                            // We're set to scroll so add the child's height plus margin
1175b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                            childScrollableHeight += child.getHeight() + childLp.topMargin
1176b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                    + childLp.bottomMargin;
1177b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes
117850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
1179b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                // For a collapsing scroll, we to take the collapsed height
1180b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                // into account.
118150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                childScrollableHeight -= ViewCompat.getMinimumHeight(child);
118250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            }
118350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
118450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1185ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        if (ViewCompat.getFitsSystemWindows(child)) {
1186ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                            childScrollableHeight -= layout.getTopInset();
1187ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        }
1188ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
118950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if (childScrollableHeight > 0) {
119050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int offsetForView = absOffset - child.getTop();
119150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int interpolatedDiff = Math.round(childScrollableHeight *
119250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                    interpolator.getInterpolation(
119350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                            offsetForView / (float) childScrollableHeight));
119450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
119550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            return Integer.signum(offset) * (child.getTop() + interpolatedDiff);
119650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
1197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
119850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
119950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // If we get to here then the view on the offset isn't suitable for interpolated
120050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // scrolling. So break out of the loop
120150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    break;
1202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return offset;
1206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1208c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes        private void updateAppBarLayoutDrawableState(final CoordinatorLayout parent,
1209df2d45b76203a9c4101629005eb27059d7f80297Chris Banes                final AppBarLayout layout, final int offset, final int direction,
1210df2d45b76203a9c4101629005eb27059d7f80297Chris Banes                final boolean forceJump) {
12111a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            final View child = getAppBarChildOnOffset(layout, offset);
12121a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            if (child != null) {
12131a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
12141a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final int flags = childLp.getScrollFlags();
12151a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                boolean collapsed = false;
12161a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
12171a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
12181a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    final int minHeight = ViewCompat.getMinimumHeight(child);
12191a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
12201a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    if (direction > 0 && (flags & (LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
12211a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                            | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED)) != 0) {
12221a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // We're set to enter always collapsed so we are only collapsed when
12231a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // being scrolled down, and in a collapsed offset
12241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
12251a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
12261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // We're set to exit until collapsed, so any offset which results in
12271a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // the minimum height (or less) being shown is collapsed
12281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
12291a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    }
12301a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                }
12311a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1232c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                final boolean changed = layout.setCollapsedState(collapsed);
1233c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes
1234df2d45b76203a9c4101629005eb27059d7f80297Chris Banes                if (Build.VERSION.SDK_INT >= 11 && (forceJump
1235df2d45b76203a9c4101629005eb27059d7f80297Chris Banes                        || (changed && shouldJumpElevationState(parent, layout)))) {
1236c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                    // If the collapsed state changed, we may need to
1237c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                    // jump to the current state if we have an overlapping view
1238c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                    layout.jumpDrawablesToCurrentState();
1239c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                }
1240c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            }
1241c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes        }
1242c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes
1243c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes        private boolean shouldJumpElevationState(CoordinatorLayout parent, AppBarLayout layout) {
1244c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            // We should jump the elevated state if we have a dependent scrolling view which has
1245c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            // an overlapping top (i.e. overlaps us)
1246c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            final List<View> dependencies = parent.getDependents(layout);
1247c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            for (int i = 0, size = dependencies.size(); i < size; i++) {
1248c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                final View dependency = dependencies.get(i);
1249c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                final CoordinatorLayout.LayoutParams lp =
1250c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                        (CoordinatorLayout.LayoutParams) dependency.getLayoutParams();
1251c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                final CoordinatorLayout.Behavior behavior = lp.getBehavior();
1252c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes
1253c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                if (behavior instanceof ScrollingViewBehavior) {
1254c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                    return ((ScrollingViewBehavior) behavior).getOverlayTop() != 0;
1255c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes                }
12561a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
1257c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes            return false;
12581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
12591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
12601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        private static View getAppBarChildOnOffset(final AppBarLayout layout, final int offset) {
12611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            final int absOffset = Math.abs(offset);
12621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
12631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final View child = layout.getChildAt(i);
12641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
12651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    return child;
12661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                }
12671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
12681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            return null;
12691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
12701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1271ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
127272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getTopBottomOffsetForScrollingSibling() {
1273e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            return getTopAndBottomOffset() + mOffsetDelta;
1274e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1275e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1276e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        @Override
1277824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes        public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout abl) {
1278824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes            final Parcelable superState = super.onSaveInstanceState(parent, abl);
1279e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final int offset = getTopAndBottomOffset();
1280e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1281e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            // Try and find the first visible child...
1282824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes            for (int i = 0, count = abl.getChildCount(); i < count; i++) {
1283824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes                View child = abl.getChildAt(i);
1284e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                final int visBottom = child.getBottom() + offset;
1285e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1286e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                if (child.getTop() + offset <= 0 && visBottom >= 0) {
1287e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    final SavedState ss = new SavedState(superState);
1288e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    ss.firstVisibleChildIndex = i;
1289c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                    ss.firstVisibleChildAtMinimumHeight =
1290824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes                            visBottom == (ViewCompat.getMinimumHeight(child) + abl.getTopInset());
1291c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                    ss.firstVisibleChildPercentageShown = visBottom / (float) child.getHeight();
1292e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    return ss;
1293e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                }
1294e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1295e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1296e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            // Else we'll just return the super state
1297e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            return superState;
1298e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1299e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1300e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        @Override
1301e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout,
1302e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                Parcelable state) {
1303e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            if (state instanceof SavedState) {
1304e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                final SavedState ss = (SavedState) state;
1305e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState());
1306e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex;
1307c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                mOffsetToChildIndexOnLayoutPerc = ss.firstVisibleChildPercentageShown;
1308c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibleChildAtMinimumHeight;
1309e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            } else {
1310e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.onRestoreInstanceState(parent, appBarLayout, state);
1311e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayout = INVALID_POSITION;
1312e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1313e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1314e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
131505f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes        protected static class SavedState extends AbsSavedState {
1316e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            int firstVisibleChildIndex;
1317c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas            float firstVisibleChildPercentageShown;
1318c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas            boolean firstVisibleChildAtMinimumHeight;
1319e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
132062ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes            public SavedState(Parcel source, ClassLoader loader) {
132105f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes                super(source, loader);
1322e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                firstVisibleChildIndex = source.readInt();
1323c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                firstVisibleChildPercentageShown = source.readFloat();
1324c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                firstVisibleChildAtMinimumHeight = source.readByte() != 0;
1325e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1326e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1327e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public SavedState(Parcelable superState) {
1328e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super(superState);
1329e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1330e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1331e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            @Override
1332e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public void writeToParcel(Parcel dest, int flags) {
1333e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.writeToParcel(dest, flags);
1334e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                dest.writeInt(firstVisibleChildIndex);
1335c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                dest.writeFloat(firstVisibleChildPercentageShown);
1336c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas                dest.writeByte((byte) (firstVisibleChildAtMinimumHeight ? 1 : 0));
1337e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1338e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
13394b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas            public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
13404b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                @Override
13414b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                public SavedState createFromParcel(Parcel source, ClassLoader loader) {
13424b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                    return new SavedState(source, loader);
13434b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                }
1344e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
13454b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                @Override
13464b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                public SavedState createFromParcel(Parcel source) {
13474b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                    return new SavedState(source, null);
13484b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                }
13494b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas
13504b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                @Override
13514b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                public SavedState[] newArray(int size) {
13524b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                    return new SavedState[size];
13534b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas                }
13544b25d63ff53d4676150ac54897851e4ff9f1049fAurimas Liutikas            };
1355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
1357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
1359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Behavior which should be used by {@link View}s which can scroll vertically and support
1360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * nested scrolling to automatically scroll any {@link AppBarLayout} siblings.
1361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
1362ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    public static class ScrollingViewBehavior extends HeaderScrollingViewBehavior {
1363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior() {}
1365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior(Context context, AttributeSet attrs) {
1367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
1368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1369b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            final TypedArray a = context.obtainStyledAttributes(attrs,
137073e7a06fb66110495fa4e98a91831a47feab2526Chris Banes                    R.styleable.ScrollingViewBehavior_Layout);
1371b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            setOverlayTop(a.getDimensionPixelSize(
137273e7a06fb66110495fa4e98a91831a47feab2526Chris Banes                    R.styleable.ScrollingViewBehavior_Layout_behavior_overlapTop, 0));
1373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
1374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
1377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
1378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We depend on any AppBarLayouts
1379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return dependency instanceof AppBarLayout;
1380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
1383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
1384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
13855e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            offsetChildAsNeeded(parent, child, dependency);
13860d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes            return false;
13870d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes        }
13880d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes
138900a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes        @Override
13904cf1d92509224fab3ca69b419a005c536ab75c5cChris Banes        public boolean onRequestChildRectangleOnScreen(CoordinatorLayout parent, View child,
139100a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                Rect rectangle, boolean immediate) {
139200a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes            final AppBarLayout header = findFirstDependency(parent.getDependencies(child));
139300a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes            if (header != null) {
139400a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                // Offset the rect by the child's left/top
139500a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                rectangle.offset(child.getLeft(), child.getTop());
139600a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes
139700a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                final Rect parentRect = mTempRect1;
139800a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                parentRect.set(0, 0, parent.getWidth(), parent.getHeight());
139900a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes
140000a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                if (!parentRect.contains(rectangle)) {
140100a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                    // If the rectangle can not be fully seen the visible bounds, collapse
140200a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                    // the AppBarLayout
140300a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                    header.setExpanded(false, !immediate);
140400a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                    return true;
140500a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                }
140600a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes            }
140700a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes            return false;
140800a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes        }
140900a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes
14105e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes        private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) {
1411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final CoordinatorLayout.Behavior behavior =
1412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
1413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (behavior instanceof Behavior) {
14145e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // Offset the child, pinning it to the bottom the header-dependency, maintaining
1415bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes                // any vertical gap and overlap
14165e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                final Behavior ablBehavior = (Behavior) behavior;
1417318baf84dade07174d71e10322e3b10ab4b0c28cChris Banes                ViewCompat.offsetTopAndBottom(child, (dependency.getBottom() - child.getTop())
14185e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                        + ablBehavior.mOffsetDelta
14195e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                        + getVerticalLayoutGap()
1420b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                        - getOverlapPixelsForOffset(dependency));
1421ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            }
1422ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        }
1423ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
1424b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        @Override
1425b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        float getOverlapRatioForOffset(final View header) {
1426b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            if (header instanceof AppBarLayout) {
1427b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                final AppBarLayout abl = (AppBarLayout) header;
1428ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                final int totalScrollRange = abl.getTotalScrollRange();
1429ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                final int preScrollDown = abl.getDownNestedPreScrollRange();
1430b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                final int offset = getAppBarLayoutOffset(abl);
1431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1432ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) {
1433ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    // If we're in a pre-scroll down. Don't use the offset at all.
1434ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    return 0;
1435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
1436ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    final int availScrollRange = totalScrollRange - preScrollDown;
1437dfb857dd12b359c0bdae11a23e039f286d54dc0aChris Banes                    if (availScrollRange != 0) {
1438ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        // Else we'll use a interpolated ratio of the overlap, depending on offset
1439b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                        return 1f + (offset / (float) availScrollRange);
1440ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    }
1441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1443b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            return 0f;
1444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1446b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        private static int getAppBarLayoutOffset(AppBarLayout abl) {
1447b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            final CoordinatorLayout.Behavior behavior =
1448b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                    ((CoordinatorLayout.LayoutParams) abl.getLayoutParams()).getBehavior();
1449b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            if (behavior instanceof Behavior) {
1450b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                return ((Behavior) behavior).getTopBottomOffsetForScrollingSibling();
1451b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            }
1452b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            return 0;
1453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1455ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
145600a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes        AppBarLayout findFirstDependency(List<View> views) {
1457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = views.size(); i < z; i++) {
1458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View view = views.get(i);
1459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof AppBarLayout) {
146000a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes                    return (AppBarLayout) view;
1461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return null;
1464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1465ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
1466ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
146772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getScrollRange(View v) {
1468ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            if (v instanceof AppBarLayout) {
1469ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                return ((AppBarLayout) v).getTotalScrollRange();
1470ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            } else {
1471ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                return super.getScrollRange(v);
1472ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            }
1473ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
1474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
1475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes}
1476