1b7f9224b1495db47eb8fd813b5912250e900770aChris Banes/*
2b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Copyright (C) 2015 The Android Open Source Project
3b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *
4b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * you may not use this file except in compliance with the License.
6b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * You may obtain a copy of the License at
7b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *
8b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *
10b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Unless required by applicable law or agreed to in writing, software
11b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * See the License for the specific language governing permissions and
14b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * limitations under the License.
15b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */
16b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
17b7f9224b1495db47eb8fd813b5912250e900770aChris Banespackage android.support.design.widget;
18b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
19b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.annotation.IntDef;
2002751b16719af2e3f8212f93c001da1b0566b1b5Chris Banesimport android.support.annotation.NonNull;
217a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banesimport android.support.v4.view.MotionEventCompat;
22b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat;
23b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.widget.ViewDragHelper;
24b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.MotionEvent;
25b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.View;
26b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.ViewGroup;
2790dfc0aa3215c247825ea1001478ed17a767c45dChris Banesimport android.view.ViewParent;
28b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
29b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.Retention;
30b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.RetentionPolicy;
31b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
32b7f9224b1495db47eb8fd813b5912250e900770aChris Banes/**
33b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * An interaction behavior plugin for child views of {@link CoordinatorLayout} to provide support
34b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * for the 'swipe-to-dismiss' gesture.
35b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */
36b7f9224b1495db47eb8fd813b5912250e900770aChris Banespublic class SwipeDismissBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
37b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
38b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
39b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * A view is not currently being dragged or animating as a result of a fling/snap.
40b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
41b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
42b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
43b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
44b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * A view is currently being dragged. The position is currently changing as a result
45b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * of user input or simulated user input.
46b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
47b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;
48b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
49b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
50b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * A view is currently settling into place as a result of a fling or
51b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * predefined non-interactive motion.
52b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
53b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;
54b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
55b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /** @hide */
56b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @IntDef({SWIPE_DIRECTION_START_TO_END, SWIPE_DIRECTION_END_TO_START, SWIPE_DIRECTION_ANY})
57b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @Retention(RetentionPolicy.SOURCE)
58b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private @interface SwipeDirection {}
59b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
60b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
61b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Swipe direction that only allows swiping in the direction of start-to-end. That is
62b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * left-to-right in LTR, or right-to-left in RTL.
63b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
64b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int SWIPE_DIRECTION_START_TO_END = 0;
65b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
66b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
67b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Swipe direction that only allows swiping in the direction of end-to-start. That is
68b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * right-to-left in LTR or left-to-right in RTL.
69b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
70b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int SWIPE_DIRECTION_END_TO_START = 1;
71b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
72b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
73b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Swipe direction which allows swiping in either direction.
74b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
75b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int SWIPE_DIRECTION_ANY = 2;
76b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
77b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static final float DEFAULT_DRAG_DISMISS_THRESHOLD = 0.5f;
78b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static final float DEFAULT_ALPHA_START_DISTANCE = 0f;
79b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static final float DEFAULT_ALPHA_END_DISTANCE = DEFAULT_DRAG_DISMISS_THRESHOLD;
80b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
81b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private ViewDragHelper mViewDragHelper;
82b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private OnDismissListener mListener;
83b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private boolean mIgnoreEvents;
84b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
85b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private float mSensitivity = 0f;
86b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private boolean mSensitivitySet;
87b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
88b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private int mSwipeDirection = SWIPE_DIRECTION_ANY;
89b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private float mDragDismissThreshold = DEFAULT_DRAG_DISMISS_THRESHOLD;
90b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private float mAlphaStartSwipeDistance = DEFAULT_ALPHA_START_DISTANCE;
91b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private float mAlphaEndSwipeDistance = DEFAULT_ALPHA_END_DISTANCE;
92b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
93b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
94b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Callback interface used to notify the application that the view has been dismissed.
95b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
96b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public interface OnDismissListener {
97b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        /**
98b7f9224b1495db47eb8fd813b5912250e900770aChris Banes         * Called when {@code view} has been dismissed via swiping.
99b7f9224b1495db47eb8fd813b5912250e900770aChris Banes         */
100b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void onDismiss(View view);
101b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
102b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        /**
103b7f9224b1495db47eb8fd813b5912250e900770aChris Banes         * Called when the drag state has changed.
104b7f9224b1495db47eb8fd813b5912250e900770aChris Banes         *
105b7f9224b1495db47eb8fd813b5912250e900770aChris Banes         * @param state the new state. One of
106b7f9224b1495db47eb8fd813b5912250e900770aChris Banes         * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
107b7f9224b1495db47eb8fd813b5912250e900770aChris Banes         */
108b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void onDragStateChanged(int state);
109b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
110b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
111b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
112b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set the listener to be used when a dismiss event occurs.
113b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
114b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param listener the listener to use.
115b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
116b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void setListener(OnDismissListener listener) {
117b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mListener = listener;
118b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
119b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
120b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
121b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Sets the swipe direction for this behavior.
122b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
123b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param direction one of the {@link #SWIPE_DIRECTION_START_TO_END},
124b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                  {@link #SWIPE_DIRECTION_END_TO_START} or {@link #SWIPE_DIRECTION_ANY}
125b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
126b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void setSwipeDirection(@SwipeDirection int direction) {
127b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mSwipeDirection = direction;
128b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
129b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
130b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
131b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set the threshold for telling if a view has been dragged enough to be dismissed.
132b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
133b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param distance a ratio of a view's width, values are clamped to 0 >= x <= 1f;
134b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
135b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void setDragDismissDistance(float distance) {
136b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mDragDismissThreshold = clamp(0f, distance, 1f);
137b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
138b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
139b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
140b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * The minimum swipe distance before the view's alpha is modified.
141b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
142b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param fraction the distance as a fraction of the view's width.
143b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
144b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void setStartAlphaSwipeDistance(float fraction) {
145b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mAlphaStartSwipeDistance = clamp(0f, fraction, 1f);
146b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
147b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
148b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
149b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * The maximum swipe distance for the view's alpha is modified.
150b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
151b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param fraction the distance as a fraction of the view's width.
152b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
153b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void setEndAlphaSwipeDistance(float fraction) {
154b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mAlphaEndSwipeDistance = clamp(0f, fraction, 1f);
155b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
156b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
157b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
158b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set the sensitivity used for detecting the start of a swipe. This only takes effect if
159b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * no touch handling has occured yet.
160b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
161b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param sensitivity Multiplier for how sensitive we should be about detecting
162b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                    the start of a drag. Larger values are more sensitive. 1.0f is normal.
163b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
164b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void setSensitivity(float sensitivity) {
165b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mSensitivity = sensitivity;
166b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mSensitivitySet = true;
167b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
168b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
169b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @Override
170b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
1717a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes        switch (MotionEventCompat.getActionMasked(event)) {
172b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            case MotionEvent.ACTION_UP:
173b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            case MotionEvent.ACTION_CANCEL:
174b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                // Reset the ignore flag
175b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (mIgnoreEvents) {
176b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    mIgnoreEvents = false;
177b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    return false;
178b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
179b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                break;
1807a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes            default:
1817a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes                mIgnoreEvents = !parent.isPointInChildBounds(child,
1827a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes                        (int) event.getX(), (int) event.getY());
1837a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes                break;
184b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
185b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
186b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (mIgnoreEvents) {
187b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return false;
188b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
189b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
190b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        ensureViewDragHelper(parent);
191b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return mViewDragHelper.shouldInterceptTouchEvent(event);
192b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
193b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
194b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @Override
195b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
196b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (mViewDragHelper != null) {
197b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mViewDragHelper.processTouchEvent(event);
198b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return true;
199b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
200b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return false;
201b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
202b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
20302751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes    /**
20402751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes     * Called when the user's input indicates that they want to swipe the given view.
20502751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes     *
20602751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes     * @param view View the user is attempting to swipe
20702751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes     * @return true if the view can be dismissed via swiping, false otherwise
20802751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes     */
20902751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes    public boolean canSwipeDismissView(@NonNull View view) {
21002751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        return true;
21102751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes    }
21202751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes
213b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
21478d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas        private static final int INVALID_POINTER_ID = -1;
21578d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas
216b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private int mOriginalCapturedViewLeft;
21778d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas        private int mActivePointerId = INVALID_POINTER_ID;
218b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
219b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
220b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public boolean tryCaptureView(View child, int pointerId) {
22178d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas            // Only capture if we don't already have an active pointer id
22278d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas            return mActivePointerId == INVALID_POINTER_ID && canSwipeDismissView(child);
22302751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        }
22402751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes
22502751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        @Override
22602751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        public void onViewCaptured(View capturedChild, int activePointerId) {
22778d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas            mActivePointerId = activePointerId;
22802751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes            mOriginalCapturedViewLeft = capturedChild.getLeft();
22990dfc0aa3215c247825ea1001478ed17a767c45dChris Banes
23090dfc0aa3215c247825ea1001478ed17a767c45dChris Banes            // The view has been captured, and thus a drag is about to start so stop any parents
23190dfc0aa3215c247825ea1001478ed17a767c45dChris Banes            // intercepting
23290dfc0aa3215c247825ea1001478ed17a767c45dChris Banes            final ViewParent parent = capturedChild.getParent();
23390dfc0aa3215c247825ea1001478ed17a767c45dChris Banes            if (parent != null) {
23490dfc0aa3215c247825ea1001478ed17a767c45dChris Banes                parent.requestDisallowInterceptTouchEvent(true);
23590dfc0aa3215c247825ea1001478ed17a767c45dChris Banes            }
236b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
237b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
238b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
239b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void onViewDragStateChanged(int state) {
240b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mListener != null) {
241b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                mListener.onDragStateChanged(state);
242b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
243b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
244b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
245b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
246b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void onViewReleased(View child, float xvel, float yvel) {
24778d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas            // Reset the active pointer ID
24878d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas            mActivePointerId = INVALID_POINTER_ID;
24978d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas
250b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final int childWidth = child.getWidth();
251b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            int targetLeft;
252b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            boolean dismiss = false;
253b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
254b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (shouldDismiss(child, xvel)) {
255b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                targetLeft = child.getLeft() < mOriginalCapturedViewLeft
256b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        ? mOriginalCapturedViewLeft - childWidth
257b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        : mOriginalCapturedViewLeft + childWidth;
258b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                dismiss = true;
259b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
260b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                // Else, reset back to the original left
261b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                targetLeft = mOriginalCapturedViewLeft;
262b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
263b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
264b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mViewDragHelper.settleCapturedViewAt(targetLeft, child.getTop())) {
265b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.postOnAnimation(child, new SettleRunnable(child, dismiss));
266b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else if (dismiss && mListener != null) {
267b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                mListener.onDismiss(child);
268b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
269b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
270b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
271b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private boolean shouldDismiss(View child, float xvel) {
272b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (xvel != 0f) {
273b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final boolean isRtl = ViewCompat.getLayoutDirection(child)
274b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        == ViewCompat.LAYOUT_DIRECTION_RTL;
275b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
276b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (mSwipeDirection == SWIPE_DIRECTION_ANY) {
277b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // We don't care about the direction so return true
278b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    return true;
279b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                } else if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
280b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // We only allow start-to-end swiping, so the fling needs to be in the
281b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // correct direction
282b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    return isRtl ? xvel < 0f : xvel > 0f;
283b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
284b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // We only allow end-to-start swiping, so the fling needs to be in the
285b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // correct direction
286b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    return isRtl ? xvel > 0f : xvel < 0f;
287b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
288b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
289b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final int distance = child.getLeft() - mOriginalCapturedViewLeft;
290b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final int thresholdDistance = Math.round(child.getWidth() * mDragDismissThreshold);
291b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                return Math.abs(distance) >= thresholdDistance;
292b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
293b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
294b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return false;
295b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
296b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
297b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
298b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public int getViewHorizontalDragRange(View child) {
299b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return child.getWidth();
300b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
301b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
302b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
303b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public int clampViewPositionHorizontal(View child, int left, int dx) {
304b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final boolean isRtl = ViewCompat.getLayoutDirection(child)
305b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    == ViewCompat.LAYOUT_DIRECTION_RTL;
306b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            int min, max;
307b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
308b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
309b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (isRtl) {
310b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    min = mOriginalCapturedViewLeft - child.getWidth();
311b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    max = mOriginalCapturedViewLeft;
312b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                } else {
313b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    min = mOriginalCapturedViewLeft;
314b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    max = mOriginalCapturedViewLeft + child.getWidth();
315b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
316b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
317b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (isRtl) {
318b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    min = mOriginalCapturedViewLeft;
319b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    max = mOriginalCapturedViewLeft + child.getWidth();
320b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                } else {
321b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    min = mOriginalCapturedViewLeft - child.getWidth();
322b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    max = mOriginalCapturedViewLeft;
323b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
324b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
325b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                min = mOriginalCapturedViewLeft - child.getWidth();
326b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                max = mOriginalCapturedViewLeft + child.getWidth();
327b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
328b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
329b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return clamp(min, left, max);
330b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
331b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
332b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
333b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public int clampViewPositionVertical(View child, int top, int dy) {
334b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return child.getTop();
335b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
336b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
337b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
338b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void onViewPositionChanged(View child, int left, int top, int dx, int dy) {
3392bda001ff9233e3dc3f8e27faf215534656d69afChris Banes            final float startAlphaDistance = mOriginalCapturedViewLeft
3402bda001ff9233e3dc3f8e27faf215534656d69afChris Banes                    + child.getWidth() * mAlphaStartSwipeDistance;
3412bda001ff9233e3dc3f8e27faf215534656d69afChris Banes            final float endAlphaDistance = mOriginalCapturedViewLeft
3422bda001ff9233e3dc3f8e27faf215534656d69afChris Banes                    + child.getWidth() * mAlphaEndSwipeDistance;
343b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
344b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (left <= startAlphaDistance) {
345b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setAlpha(child, 1f);
346b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else if (left >= endAlphaDistance) {
347b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setAlpha(child, 0f);
348b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
349b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                // We're between the start and end distances
350b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final float distance = fraction(startAlphaDistance, endAlphaDistance, left);
351b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setAlpha(child, clamp(0f, 1f - distance, 1f));
352b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
353b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
354b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    };
355b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
356b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private void ensureViewDragHelper(ViewGroup parent) {
357b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (mViewDragHelper == null) {
358b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mViewDragHelper = mSensitivitySet
359b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    ? ViewDragHelper.create(parent, mSensitivity, mDragCallback)
360b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    : ViewDragHelper.create(parent, mDragCallback);
361b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
362b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
363b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
364b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private class SettleRunnable implements Runnable {
365b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private final View mView;
366b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private final boolean mDismiss;
367b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
368b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        SettleRunnable(View view, boolean dismiss) {
369b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mView = view;
370b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mDismiss = dismiss;
371b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
372b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
373b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
374b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void run() {
375b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
376b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.postOnAnimation(mView, this);
377b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
378b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (mDismiss && mListener != null) {
379b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    mListener.onDismiss(mView);
380b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
381b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
382b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
383b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
384b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
385b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static float clamp(float min, float value, float max) {
386b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return Math.min(Math.max(min, value), max);
387b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
388b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
389b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static int clamp(int min, int value, int max) {
390b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return Math.min(Math.max(min, value), max);
391b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
392b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
393b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
394b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Retrieve the current drag state of this behavior. This will return one of
395b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
396b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
397b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @return The current drag state
398b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
399b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public int getDragState() {
400b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return mViewDragHelper != null ? mViewDragHelper.getViewDragState() : STATE_IDLE;
401b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
402b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
403b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
404b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * The fraction that {@code value} is between {@code startValue} and {@code endValue}.
405b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
406b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    static float fraction(float startValue, float endValue, float value) {
407b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return (value - startValue) / (endValue - startValue);
408b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
409b7f9224b1495db47eb8fd813b5912250e900770aChris Banes}