BottomSheetBehavior.java revision 4eb453c75e552a2e0231a93468d9597a4069286e
1452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki/*
2452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Copyright (C) 2015 The Android Open Source Project
3452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki *
4452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Licensed under the Apache License, Version 2.0 (the "License");
5452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * you may not use this file except in compliance with the License.
6452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * You may obtain a copy of the License at
7452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki *
8452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki *      http://www.apache.org/licenses/LICENSE-2.0
9452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki *
10452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Unless required by applicable law or agreed to in writing, software
11452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * distributed under the License is distributed on an "AS IS" BASIS,
12452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * See the License for the specific language governing permissions and
14452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * limitations under the License.
15452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */
16452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
17452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakipackage android.support.design.widget;
18452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
19452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.content.Context;
20452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.content.res.TypedArray;
21452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.os.Parcel;
22452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.os.Parcelable;
23452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.annotation.IntDef;
24dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Arakiimport android.support.annotation.NonNull;
25452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.design.R;
26452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.MotionEventCompat;
279aec720809a123c6193304730acf8b55d6ce5a7aYuichi Arakiimport android.support.v4.view.NestedScrollingChild;
28e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.support.v4.view.VelocityTrackerCompat;
29452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.ViewCompat;
30452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.widget.ViewDragHelper;
31452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.util.AttributeSet;
32452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.MotionEvent;
33e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.VelocityTracker;
34452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.View;
35e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.ViewConfiguration;
36452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.ViewGroup;
37452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
38452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.Retention;
39452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.RetentionPolicy;
40452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.ref.WeakReference;
41452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
42452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
43452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki/**
44452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as
45452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * a bottom sheet.
46452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */
47452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakipublic class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
48452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
49452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
504df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki     * Callback for monitoring events about bottom sheets.
51f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     */
524df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki    public abstract static class BottomSheetCallback {
53f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
54f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        /**
55f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         * Called when the bottom sheet changes its state.
56f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         *
57dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki         * @param bottomSheet The bottom sheet view.
58dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki         * @param newState    The new state. This will be one of {@link #STATE_DRAGGING},
59dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki         *                    {@link #STATE_SETTLING}, {@link #STATE_EXPANDED},
60dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki         *                    {@link #STATE_COLLAPSED}, or {@link #STATE_HIDDEN}.
61f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         */
62dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki        public abstract void onStateChanged(@NonNull View bottomSheet, @State int newState);
63f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
64f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        /**
65f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         * Called when the bottom sheet is being dragged.
66f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         *
67dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki         * @param bottomSheet The bottom sheet view.
68f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         * @param slideOffset The new offset of this bottom sheet within its range, from 0 to 1
69f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         *                    when it is moving upward, and from 0 to -1 when it moving downward.
70f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki         */
71dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki        public abstract void onSlide(@NonNull View bottomSheet, float slideOffset);
72f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    }
73f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
74f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    /**
75452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * The bottom sheet is dragging.
76452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
77452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public static final int STATE_DRAGGING = 1;
78452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
79452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
80452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * The bottom sheet is settling.
81452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
82452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public static final int STATE_SETTLING = 2;
83452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
84452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
85452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * The bottom sheet is expanded.
86452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
87452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public static final int STATE_EXPANDED = 3;
88452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
89452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
90452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * The bottom sheet is collapsed.
91452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
92452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public static final int STATE_COLLAPSED = 4;
93452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
94f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    /**
95f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * The bottom sheet is hidden.
96f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     */
97f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    public static final int STATE_HIDDEN = 5;
98f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
99452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /** @hide */
100f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN})
101452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @Retention(RetentionPolicy.SOURCE)
102452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public @interface State {}
103452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
104f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    private static final float HIDE_THRESHOLD = 0.5f;
105f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
106f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    private static final float HIDE_FRICTION = 0.1f;
107f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
108e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    private float mMaximumVelocity;
109f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
110452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private int mPeekHeight;
111452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
112452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private int mMinOffset;
113452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
114452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private int mMaxOffset;
115452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
116f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    private boolean mHideable;
117f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
118452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @State
119452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private int mState = STATE_COLLAPSED;
120452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
121452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private ViewDragHelper mViewDragHelper;
122452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
123452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private boolean mIgnoreEvents;
124452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
125e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    private int mLastNestedScrollDy;
126e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
12749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki    private boolean mNestedScrolled;
12849a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki
129452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private int mParentHeight;
130452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
131452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private WeakReference<V> mViewRef;
132452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
1339aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki    private WeakReference<View> mNestedScrollingChildRef;
134f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
1359aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki    private BottomSheetCallback mCallback;
136f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
137e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    private VelocityTracker mVelocityTracker;
138e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
139e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    private int mActivePointerId;
140f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
1414eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki    private int mInitialY;
1424eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki
143452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
144452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * Default constructor for instantiating BottomSheetBehaviors.
145452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
146452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public BottomSheetBehavior() {
147452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
148452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
149452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
150452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * Default constructor for inflating BottomSheetBehaviors from layout.
151452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     *
152452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @param context The {@link Context}.
153452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @param attrs   The {@link AttributeSet}.
154452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
155452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public BottomSheetBehavior(Context context, AttributeSet attrs) {
156452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        super(context, attrs);
157452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        TypedArray a = context.obtainStyledAttributes(attrs,
158452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                R.styleable.BottomSheetBehavior_Params);
159452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        setPeekHeight(a.getDimensionPixelSize(
160452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                R.styleable.BottomSheetBehavior_Params_behavior_peekHeight, 0));
161f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        setHideable(a.getBoolean(R.styleable.BottomSheetBehavior_Params_behavior_hideable, false));
162452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        a.recycle();
163e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        ViewConfiguration configuration = ViewConfiguration.get(context);
164e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
165452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
166452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
167452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @Override
168452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
169452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        return new SavedState(super.onSaveInstanceState(parent, child), mState);
170452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
171452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
172452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @Override
173452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
174452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        SavedState ss = (SavedState) state;
175452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        super.onRestoreInstanceState(parent, child, ss.getSuperState());
176452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        // Intermediate states are restored as collapsed state
177f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) {
178452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            mState = STATE_COLLAPSED;
179f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        } else {
180f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            mState = ss.state;
181452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
182452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
183452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
184452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @Override
185452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
186452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        // First let the parent lay it out
187f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        if (mState != STATE_DRAGGING && mState != STATE_SETTLING) {
188f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            parent.onLayoutChild(child, layoutDirection);
189f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        }
190452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        // Offset the bottom sheet
191452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mParentHeight = parent.getHeight();
192452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mMinOffset = Math.max(0, mParentHeight - child.getHeight());
193452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mMaxOffset = mParentHeight - mPeekHeight;
194452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (mState == STATE_EXPANDED) {
195452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            ViewCompat.offsetTopAndBottom(child, mMinOffset);
196f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        } else if (mHideable && mState == STATE_HIDDEN) {
197f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            ViewCompat.offsetTopAndBottom(child, mParentHeight);
198f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        } else if (mState == STATE_COLLAPSED) {
199452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            ViewCompat.offsetTopAndBottom(child, mMaxOffset);
200452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
201452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (mViewDragHelper == null) {
202452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
203452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
204452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mViewRef = new WeakReference<>(child);
2059aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
206452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        return true;
207452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
208452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
209452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @Override
210452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
21117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki        if (!child.isShown()) {
21217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki            return false;
21317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki        }
214452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        int action = MotionEventCompat.getActionMasked(event);
215e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        // Record the velocity
216e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (action == MotionEvent.ACTION_DOWN) {
217e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            reset();
218e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
219e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (mVelocityTracker == null) {
220e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            mVelocityTracker = VelocityTracker.obtain();
221e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
222e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        mVelocityTracker.addMovement(event);
223452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        switch (action) {
224452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            case MotionEvent.ACTION_UP:
225452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            case MotionEvent.ACTION_CANCEL:
226e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                mActivePointerId = MotionEvent.INVALID_POINTER_ID;
227452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                // Reset the ignore flag
228452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                if (mIgnoreEvents) {
229452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    mIgnoreEvents = false;
230452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    return false;
231452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                }
232452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                break;
233452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            case MotionEvent.ACTION_DOWN:
2344eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                int initialX = (int) event.getX();
2354eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                mInitialY = (int) event.getY();
2369aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                View scroll = mNestedScrollingChildRef.get();
2374eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                if (scroll != null && parent.isPointInChildBounds(scroll, initialX, mInitialY)) {
238e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                    mActivePointerId = event.getPointerId(event.getActionIndex());
2399aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                }
240e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID &&
2414eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                        !parent.isPointInChildBounds(child, initialX, mInitialY);
242452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                break;
243452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
2444eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        if (!mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event)) {
2454eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki            return true;
2464eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        }
2474eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        // We have to handle cases that the ViewDragHelper does not capture the bottom sheet because
2484eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        // it is not the top most view of its parent. This is not necessary when the touch event is
2494eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        // happening over the scrolling content as nested scrolling logic handles that case.
2504eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        View scroll = mNestedScrollingChildRef.get();
2514eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        return action == MotionEvent.ACTION_MOVE && scroll != null &&
2524eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                !mIgnoreEvents && mState != STATE_DRAGGING &&
2534eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                !parent.isPointInChildBounds(scroll, (int) event.getX(), (int) event.getY()) &&
2544eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop();
255452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
256452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
257452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @Override
258452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
25917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki        if (!child.isShown()) {
26017fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki            return false;
26117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki        }
262e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        int action = MotionEventCompat.getActionMasked(event);
263e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) {
264e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            return true;
265e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
266452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mViewDragHelper.processTouchEvent(event);
267e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        // Record the velocity
268e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (action == MotionEvent.ACTION_DOWN) {
269e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            reset();
270e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
271e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (mVelocityTracker == null) {
272e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            mVelocityTracker = VelocityTracker.obtain();
273e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
274e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        mVelocityTracker.addMovement(event);
2754eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
2764eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        // to capture the bottom sheet in case it is not captured and the touch slop is passed.
2774eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        if (action == MotionEvent.ACTION_MOVE) {
2784eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki            if (Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) {
2794eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki                mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex()));
2804eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki            }
2814eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki        }
282452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        return true;
283452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
284452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
285e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    @Override
286e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child,
287e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            View directTargetChild, View target, int nestedScrollAxes) {
288e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        mLastNestedScrollDy = 0;
28949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki        mNestedScrolled = false;
290e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
291e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    }
292e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
293e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    @Override
294e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx,
295e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            int dy, int[] consumed) {
296e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        View scrollingChild = mNestedScrollingChildRef.get();
297e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (target != scrollingChild) {
298e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            return;
299e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
300e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        int currentTop = child.getTop();
301e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        int newTop = currentTop - dy;
302e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (dy > 0) { // Upward
303e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            if (newTop < mMinOffset) {
304e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                consumed[1] = currentTop - mMinOffset;
305e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                ViewCompat.offsetTopAndBottom(child, -consumed[1]);
306e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                setStateInternal(STATE_EXPANDED);
307e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            } else {
308e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                consumed[1] = dy;
309e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                ViewCompat.offsetTopAndBottom(child, -dy);
310e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                setStateInternal(STATE_DRAGGING);
311e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            }
312e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        } else if (dy < 0) { // Downward
313e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            if (!ViewCompat.canScrollVertically(target, -1)) {
314e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                if (newTop <= mMaxOffset || mHideable) {
315e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                    consumed[1] = dy;
316e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                    ViewCompat.offsetTopAndBottom(child, -dy);
317e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                    setStateInternal(STATE_DRAGGING);
318e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                } else {
319e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                    consumed[1] = currentTop - mMaxOffset;
320e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                    ViewCompat.offsetTopAndBottom(child, -consumed[1]);
321e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                    setStateInternal(STATE_COLLAPSED);
322e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                }
323e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            }
324e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
325e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        dispatchOnSlide(child.getTop());
326e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        mLastNestedScrollDy = dy;
32749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki        mNestedScrolled = true;
328e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    }
329e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
330e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    @Override
331e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
332e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (child.getTop() == mMinOffset || target != mNestedScrollingChildRef.get() ||
33349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                !mNestedScrolled) {
334e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            return;
335e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
336e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        int top;
337e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        int targetState;
338e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (mLastNestedScrollDy > 0) {
339e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            top = mMinOffset;
340e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            targetState = STATE_EXPANDED;
341e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        } else if (mHideable && shouldHide(child, getYVelocity())) {
342e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            top = mParentHeight;
343e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            targetState = STATE_HIDDEN;
34449a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki        } else if (mLastNestedScrollDy == 0) {
34549a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki            int currentTop = child.getTop();
34649a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki            if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
34749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                top = mMinOffset;
34849a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                targetState = STATE_EXPANDED;
34949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki            } else {
35049a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                top = mMaxOffset;
35149a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                targetState = STATE_COLLAPSED;
35249a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki            }
353e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        } else {
354e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            top = mMaxOffset;
355e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            targetState = STATE_COLLAPSED;
356e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
357e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
358e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            setStateInternal(STATE_SETTLING);
359e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
360e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        } else {
361e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            setStateInternal(targetState);
362e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
36349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki        mNestedScrolled = false;
364e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    }
365e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
366e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    @Override
367e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
368e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            float velocityX, float velocityY) {
369e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        return target == mNestedScrollingChildRef.get() &&
370e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                (mState != STATE_EXPANDED ||
371e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                        super.onNestedPreFling(coordinatorLayout, child, target,
372e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                                velocityX, velocityY));
373e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    }
374e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
375452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
376452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * Sets the height of the bottom sheet when it is collapsed.
377452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     *
378452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @param peekHeight The height of the collapsed bottom sheet in pixels.
379452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight
380452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
381452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public final void setPeekHeight(int peekHeight) {
382452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mPeekHeight = Math.max(0, peekHeight);
383452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mMaxOffset = mParentHeight - peekHeight;
384452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
385452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
386452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
387452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * Gets the height of the bottom sheet when it is collapsed.
388452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     *
389452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @return The height of the collapsed bottom sheet.
390452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight
391452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
392452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public final int getPeekHeight() {
393452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        return mPeekHeight;
394452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
395452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
396452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
397f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * Sets whether this bottom sheet can hide when it is swiped down.
398f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     *
399f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * @param hideable {@code true} to make this bottom sheet hideable.
400f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_hideable
401f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     */
402f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    public void setHideable(boolean hideable) {
403f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        mHideable = hideable;
404f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    }
405f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
406f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    /**
407f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * Gets whether this bottom sheet can hide when it is swiped down.
408f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     *
409f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * @return {@code true} if this bottom sheet can hide.
410f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_hideable
411f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     */
412f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    public boolean isHideable() {
413f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        return mHideable;
414f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    }
415f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
416f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    /**
4174df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki     * Sets a callback to be notified of bottom sheet events.
418f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     *
4194df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki     * @param callback The callback to notify when bottom sheet events occur.
420f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     */
4214df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki    public void setBottomSheetCallback(BottomSheetCallback callback) {
4224df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki        mCallback = callback;
423f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    }
424f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
425f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    /**
426452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * Sets the state of the bottom sheet. The bottom sheet will transition to that state with
427452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * animation.
428452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     *
429f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED}, or
430f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki     *              {@link #STATE_HIDDEN}.
431452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
432452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public final void setState(@State int state) {
433452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        V child = mViewRef.get();
434452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (child == null) {
435452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            return;
436452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
437452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        int top;
438452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (state == STATE_COLLAPSED) {
439452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            top = mMaxOffset;
440452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        } else if (state == STATE_EXPANDED) {
441452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            top = mMinOffset;
442f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        } else if (mHideable && state == STATE_HIDDEN) {
443f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            top = mParentHeight;
444452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        } else {
445452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            throw new IllegalArgumentException("Illegal state argument: " + state);
446452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
447452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        setStateInternal(STATE_SETTLING);
448452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
449452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
450452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
451452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
452452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
453452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    /**
454452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * Gets the current state of the bottom sheet.
455452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     *
456452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING},
457452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * and {@link #STATE_SETTLING}.
458452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
459452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @State
460452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public final int getState() {
461452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        return mState;
462452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
463452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
464452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private void setStateInternal(@State int state) {
465452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (mState == state) {
466452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            return;
467452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
468452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        mState = state;
469dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki        View bottomSheet = mViewRef.get();
470dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki        if (bottomSheet != null && mCallback != null) {
471dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki            mCallback.onStateChanged(bottomSheet, state);
472f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        }
473f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    }
474f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
475e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    private void reset() {
476e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        mActivePointerId = ViewDragHelper.INVALID_POINTER;
477e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        if (mVelocityTracker != null) {
478e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            mVelocityTracker.recycle();
479e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            mVelocityTracker = null;
480e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        }
481e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    }
482e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
483f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    private boolean shouldHide(View child, float yvel) {
484f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        if (child.getTop() < mMaxOffset) {
485f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            // It should not hide, but collapse.
486f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            return false;
487f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        }
488f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        final float newTop = child.getTop() + yvel * HIDE_FRICTION;
489f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD;
490f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    }
491f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
4929aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki    private View findScrollingChild(View view) {
4939aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        if (view instanceof NestedScrollingChild) {
4949aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            return view;
4959aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        }
4969aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        if (view instanceof ViewGroup) {
4979aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            ViewGroup group = (ViewGroup) view;
4989aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            for (int i = 0, count = group.getChildCount(); i < count; i++) {
4999aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                View scrollingChild = findScrollingChild(group.getChildAt(i));
5009aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                if (scrollingChild != null) {
5019aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                    return scrollingChild;
5029aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                }
5039aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            }
5049aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        }
5059aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        return null;
506452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
507452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
508e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    private float getYVelocity() {
509e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
510e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki        return VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId);
511e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki    }
512e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki
513452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
514452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
515452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @Override
516452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public boolean tryCaptureView(View child, int pointerId) {
517e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            if (mState == STATE_DRAGGING) {
518e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki                return false;
519e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            }
520e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki            if (mState == STATE_EXPANDED && mActivePointerId == pointerId) {
5219aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                View scroll = mNestedScrollingChildRef.get();
5229aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                if (scroll != null && ViewCompat.canScrollVertically(scroll, -1)) {
5239aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                    // Let the content scroll up
5249aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                    return false;
5259aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                }
5269aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            }
527f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            return mViewRef != null && mViewRef.get() == child;
528f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        }
529f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
530f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        @Override
531f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
532f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            dispatchOnSlide(top);
533452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
534452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
535452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @Override
536452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public void onViewDragStateChanged(int state) {
537452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            if (state == ViewDragHelper.STATE_DRAGGING) {
538452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                setStateInternal(STATE_DRAGGING);
539452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            }
540452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
541452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
542452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @Override
543452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public void onViewReleased(View releasedChild, float xvel, float yvel) {
544452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            int top;
545452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            @State int targetState;
546f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            if (yvel < 0) { // Moving up
547452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                top = mMinOffset;
548452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                targetState = STATE_EXPANDED;
549f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            } else if (mHideable && shouldHide(releasedChild, yvel)) {
550f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki                top = mParentHeight;
551f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki                targetState = STATE_HIDDEN;
55249a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki            } else if (yvel == 0.f) {
55349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                int currentTop = releasedChild.getTop();
55449a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
55549a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                    top = mMinOffset;
55649a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                    targetState = STATE_EXPANDED;
55749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                } else {
55849a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                    top = mMaxOffset;
55949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                    targetState = STATE_COLLAPSED;
56049a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki                }
561452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            } else {
562452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                top = mMaxOffset;
563452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                targetState = STATE_COLLAPSED;
564452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            }
565452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
566f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki                setStateInternal(STATE_SETTLING);
567452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                ViewCompat.postOnAnimation(releasedChild,
568452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                        new SettleRunnable(releasedChild, targetState));
569f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            } else {
570f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki                setStateInternal(targetState);
571452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            }
572452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
573452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
574452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @Override
575452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public int clampViewPositionVertical(View child, int top, int dy) {
576f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            return MathUtils.constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
577452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
578452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
579452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @Override
580452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public int clampViewPositionHorizontal(View child, int left, int dx) {
581452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            return child.getLeft();
582452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
583f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
5849aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        @Override
5859aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        public int getViewVerticalDragRange(View child) {
5869aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            if (mHideable) {
5879aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                return mParentHeight - mMinOffset;
5889aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            } else {
5899aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki                return mMaxOffset - mMinOffset;
5909aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki            }
5919aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki        }
592452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    };
593452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
594f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    private void dispatchOnSlide(int top) {
595dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki        View bottomSheet = mViewRef.get();
596dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki        if (bottomSheet != null && mCallback != null) {
597f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            if (top > mMaxOffset) {
598dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki                mCallback.onSlide(bottomSheet, (float) (mMaxOffset - top) / mPeekHeight);
599f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            } else {
600dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki                mCallback.onSlide(bottomSheet,
601dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki                        (float) (mMaxOffset - top) / ((mMaxOffset - mMinOffset)));
602f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki            }
603f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki        }
604f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    }
605f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki
606452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    private class SettleRunnable implements Runnable {
607452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
608452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        private final View mView;
609452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
610452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @State
611452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        private final int mTargetState;
612452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
613452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        SettleRunnable(View view, @State int targetState) {
614452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            mView = view;
615452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            mTargetState = targetState;
616452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
617452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
618452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @Override
619452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public void run() {
620452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
621452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                ViewCompat.postOnAnimation(mView, this);
622452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            } else {
623452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                setStateInternal(mTargetState);
624452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            }
625452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
626452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
627452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
628452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    protected static class SavedState extends View.BaseSavedState {
629452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
630452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @State
631452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        final int state;
632452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
633452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public SavedState(Parcel source) {
634452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            super(source);
635452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            //noinspection ResourceType
636452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            state = source.readInt();
637452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
638452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
639452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public SavedState(Parcelable superState, @State int state) {
640452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            super(superState);
641452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            this.state = state;
642452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
643452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
644452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        @Override
645452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public void writeToParcel(Parcel out, int flags) {
646452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            super.writeToParcel(out, flags);
647452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            out.writeInt(state);
648452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
649452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
650452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        public static final Parcelable.Creator<SavedState> CREATOR =
651452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                new Parcelable.Creator<SavedState>() {
652452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    @Override
653452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    public SavedState createFromParcel(Parcel source) {
654452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                        return new SavedState(source);
655452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    }
656452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
657452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    @Override
658452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    public SavedState[] newArray(int size) {
659452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                        return new SavedState[size];
660452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    }
661452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                };
662452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
663452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
664f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki    /**
665452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * A utility function to get the {@link BottomSheetBehavior} associated with the {@code view}.
666452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     *
667452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @param view The {@link View} with {@link BottomSheetBehavior}.
668452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     * @return The {@link BottomSheetBehavior} associated with the {@code view}.
669452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki     */
670452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    @SuppressWarnings("unchecked")
671452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    public static <V extends View> BottomSheetBehavior<V> from(V view) {
672452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        ViewGroup.LayoutParams params = view.getLayoutParams();
673452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (!(params instanceof CoordinatorLayout.LayoutParams)) {
674452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
675452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
676452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
677452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                .getBehavior();
678452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        if (!(behavior instanceof BottomSheetBehavior)) {
679452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki            throw new IllegalArgumentException(
680452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki                    "The view is not associated with BottomSheetBehavior");
681452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        }
682452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki        return (BottomSheetBehavior<V>) behavior;
683452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki    }
684452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki
685452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki}
686