ItemTouchHelper.java revision 3a500f61a8bdf48904f380f2d4925fe420d18ce7
1ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent/*
2ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * Copyright (C) 2015 The Android Open Source Project
3ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent *
4ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * Licensed under the Apache License, Version 2.0 (the "License");
5ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * you may not use this file except in compliance with the License.
6ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * You may obtain a copy of the License at
7ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent *
8ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent *      http://www.apache.org/licenses/LICENSE-2.0
9ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent *
10ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * Unless required by applicable law or agreed to in writing, software
11ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * distributed under the License is distributed on an "AS IS" BASIS,
12ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * See the License for the specific language governing permissions and
14ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * limitations under the License.
15ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent */
16ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
17ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentpackage android.support.v7.widget.helper;
18ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
19ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.content.res.Resources;
20ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.graphics.Canvas;
21ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.graphics.Rect;
22153b9fe667e6e78e0218ff0159353097428c7657Glenn Kastenimport android.os.Build;
23ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.annotation.Nullable;
24ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.animation.AnimatorCompatHelper;
25ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.animation.AnimatorListenerCompat;
26ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.animation.AnimatorUpdateListenerCompat;
27ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.animation.ValueAnimatorCompat;
28ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.view.GestureDetectorCompat;
29ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.view.MotionEventCompat;
30ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.view.VelocityTrackerCompat;
31ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v4.view.ViewCompat;
32ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v7.recyclerview.R;
33ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v7.widget.LinearLayoutManager;
34ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v7.widget.RecyclerView;
35ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v7.widget.RecyclerView.OnItemTouchListener;
36ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.support.v7.widget.RecyclerView.ViewHolder;
37ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.util.Log;
38ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.GestureDetector;
39ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.HapticFeedbackConstants;
40ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.MotionEvent;
41ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.VelocityTracker;
42ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.View;
43ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.ViewConfiguration;
44ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.ViewParent;
45ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport android.view.animation.Interpolator;
46ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
47ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport java.util.ArrayList;
48ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentimport java.util.List;
49ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
50ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent/**
51ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
52ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * <p>
53ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * It works with a RecyclerView and a Callback class, which configures what type of interactions
54ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * are enabled and also receives events when user performs these actions.
55ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * <p>
56ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * Depending on which functionality you support, you should override
57ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or
58ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * {@link Callback#onSwiped(ViewHolder, int)}.
59ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * <p>
60ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * This class is designed to work with any LayoutManager but for certain situations, it can be
61ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * optimized for your custom LayoutManager by extending methods in the
62ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * {@link ItemTouchHelper.Callback} class or implementing {@link ItemTouchHelper.ViewDropHandler}
63ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * interface in your LayoutManager.
64ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * <p>
65ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. On
66ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * platforms older than Honeycomb, ItemTouchHelper uses canvas translations and View's visibility
67ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * property to move items in response to touch events. You can customize these behaviors by
68ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * overriding {@link Callback#onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,
69ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * boolean)}
70ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * or {@link Callback#onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
71ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * boolean)}.
72ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * <p/>
73ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * Most of the time, you only need to override <code>onChildDraw</code> but due to limitations of
74ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent * platform prior to Honeycomb, you may need to implement <code>onChildDrawOver</code> as well.
75ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent */
76ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurentpublic class ItemTouchHelper extends RecyclerView.ItemDecoration
77ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        implements RecyclerView.OnChildAttachStateChangeListener {
78ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
79ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
80ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Up direction, used for swipe & drag control.
81ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
82ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int UP = 1;
83ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
84ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
85ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Down direction, used for swipe & drag control.
86ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
87ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int DOWN = 1 << 1;
88ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
89ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
90ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Left direction, used for swipe & drag control.
91ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
92ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int LEFT = 1 << 2;
93ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
94ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
95ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Right direction, used for swipe & drag control.
96ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
97ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int RIGHT = 1 << 3;
98bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent
99ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    // If you change these relative direction values, update Callback#convertToAbsoluteDirection,
100ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    // Callback#convertToRelativeDirection.
101ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
102ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Horizontal start direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout
103ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * direction. Used for swipe & drag control.
104ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
105ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int START = LEFT << 2;
106ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
107ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
108ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Horizontal end direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout
109ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * direction. Used for swipe & drag control.
110ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
111ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int END = RIGHT << 2;
112ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
113ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
114ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * ItemTouchHelper is in idle state. At this state, either there is no related motion event by
115ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * the user or latest motion events have not yet triggered a swipe or drag.
116ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
117ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int ACTION_STATE_IDLE = 0;
118ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1196e2ebe97f2ad0a21907f20f9ee644c4eacbb7a40Glenn Kasten    /**
120ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * A View is currently being swiped.
1216e2ebe97f2ad0a21907f20f9ee644c4eacbb7a40Glenn Kasten     */
122ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int ACTION_STATE_SWIPE = 1;
123ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
124ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
125ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * A View is currently being dragged.
126ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
127ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int ACTION_STATE_DRAG = 2;
128ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
129ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
130ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Animation type for views which are swiped successfully.
131ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
132ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 1 << 1;
133ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
134ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
135ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Animation type for views which are not completely swiped thus will animate back to their
136ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * original position.
137ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
138ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int ANIMATION_TYPE_SWIPE_CANCEL = 1 << 2;
139ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
140ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
141ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Animation type for views that were dragged and now will animate to their final position.
142ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
143ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static final int ANIMATION_TYPE_DRAG = 1 << 3;
144ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
145ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    static final String TAG = "ItemTouchHelper";
146ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
147ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    static final boolean DEBUG = false;
148ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
149ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    static final int ACTIVE_POINTER_ID_NONE = -1;
150ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
151ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    static final int DIRECTION_FLAG_COUNT = 8;
152ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
153ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private static final int ACTION_MODE_IDLE_MASK = (1 << DIRECTION_FLAG_COUNT) - 1;
154ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
155ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    static final int ACTION_MODE_SWIPE_MASK = ACTION_MODE_IDLE_MASK << DIRECTION_FLAG_COUNT;
156ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
157ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    static final int ACTION_MODE_DRAG_MASK = ACTION_MODE_SWIPE_MASK << DIRECTION_FLAG_COUNT;
158ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
159ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
160ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * The unit we are using to track velocity
161ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
162ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private static final int PIXELS_PER_SECOND = 1000;
163ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
164ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
165ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Views, whose state should be cleared after they are detached from RecyclerView.
166ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * This is necessary after swipe dismissing an item. We wait until animator finishes its job
167ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * to clean these views.
168ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
169ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    final List<View> mPendingCleanup = new ArrayList<View>();
170ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
171ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
172ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Re-use array to calculate dx dy for a ViewHolder
173ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
174ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private final float[] mTmpPosition = new float[2];
175ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
176ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
177ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Currently selected view holder
178ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
179ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    ViewHolder mSelected = null;
180ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
181ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
182ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * The reference coordinates for the action start. For drag & drop, this is the time long
183ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * press is completed vs for swipe, this is the initial touch point.
184ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
185ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mInitialTouchX;
186ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
187ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mInitialTouchY;
188ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
189ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
190ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Set when ItemTouchHelper is assigned to a RecyclerView.
191ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
192ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mSwipeEscapeVelocity;
193ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
194ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
195ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Set when ItemTouchHelper is assigned to a RecyclerView.
196ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
197ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mMaxSwipeVelocity;
198ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
199ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
200ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * The diff between the last event and initial touch.
201ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
202ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mDx;
203ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
204ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mDy;
205ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
206ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
207ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * The coordinates of the selected view at the time it is selected. We record these values
208ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * when action starts so that we can consistently position it even if LayoutManager moves the
209ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * View.
210ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
211ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mSelectedStartX;
212ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
213ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    float mSelectedStartY;
214ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
215ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
216ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * The pointer we are tracking.
217ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
218ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    int mActivePointerId = ACTIVE_POINTER_ID_NONE;
219ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
220ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
221d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent     * Developer callback which controls the behavior of ItemTouchHelper.
222d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent     */
223d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    Callback mCallback;
224d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent
225d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    /**
226ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Current mode.
227ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
228d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    int mActionState = ACTION_STATE_IDLE;
229d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent
230d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    /**
231d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent     * The direction flags obtained from unmasking
232d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent     * {@link Callback#getAbsoluteMovementFlags(RecyclerView, ViewHolder)} for the current
233ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * action state.
234ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
235ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    int mSelectedFlags;
236ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
237ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
238ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * When a View is dragged or swiped and needs to go back to where it was, we create a Recover
239ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Animation and animate it to its location using this custom Animator, instead of using
240ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * framework Animators.
241ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Using framework animators has the side effect of clashing with ItemAnimator, creating
242ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * jumpy UIs.
243ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
244ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    List<RecoverAnimation> mRecoverAnimations = new ArrayList<RecoverAnimation>();
245ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
246ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private int mSlop;
247ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
248ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    RecyclerView mRecyclerView;
249ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
250ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
251ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * When user drags a view to the edge, we start scrolling the LayoutManager as long as View
252ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * is partially out of bounds.
253ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
254ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    final Runnable mScrollRunnable = new Runnable() {
255ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        @Override
256ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public void run() {
257ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mSelected != null && scrollIfNecessary()) {
258ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (mSelected != null) { //it might be lost during scrolling
259ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    moveIfNecessary(mSelected);
260ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
261ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mRecyclerView.removeCallbacks(mScrollRunnable);
262ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ViewCompat.postOnAnimation(mRecyclerView, this);
263ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
264ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
265ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    };
266ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
267ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
268ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Used for detecting fling swipe
269ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
270ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    VelocityTracker mVelocityTracker;
271ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
272ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    //re-used list for selecting a swap target
273ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private List<ViewHolder> mSwapTargets;
274ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
275ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    //re used for for sorting swap targets
276ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private List<Integer> mDistances;
277ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
278ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
279ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * If drag & drop is supported, we use child drawing order to bring them to front.
280ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
281ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private RecyclerView.ChildDrawingOrderCallback mChildDrawingOrderCallback = null;
282ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
283ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
284ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * This keeps a reference to the child dragged by the user. Even after user stops dragging,
285ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * until view reaches its final position (end of recover animation), we keep a reference so
286ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * that it can be drawn above other children.
287ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
288ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    View mOverdrawChild = null;
289ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
290ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
291ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * We cache the position of the overdraw child to avoid recalculating it each time child
292ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * position callback is called. This value is invalidated whenever a child is attached or
293ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * detached.
294ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
295ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    int mOverdrawChildPosition = -1;
296ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
297ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
298ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Used to detect long press.
299d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent     */
300ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    GestureDetectorCompat mGestureDetector;
301ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
302ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private final OnItemTouchListener mOnItemTouchListener
303ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            = new OnItemTouchListener() {
304ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        @Override
305ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {
306ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mGestureDetector.onTouchEvent(event);
307d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            if (DEBUG) {
308d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event);
309d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            }
310d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            final int action = MotionEventCompat.getActionMasked(event);
311d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            if (action == MotionEvent.ACTION_DOWN) {
312ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mActivePointerId = event.getPointerId(0);
313d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                mInitialTouchX = event.getX();
314d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                mInitialTouchY = event.getY();
315ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                obtainVelocityTracker();
316ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (mSelected == null) {
317d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                    final RecoverAnimation animation = findAnimation(event);
318ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (animation != null) {
319d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                        mInitialTouchX -= animation.mX;
320d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                        mInitialTouchY -= animation.mY;
321ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        endRecoverAnimation(animation.mViewHolder, true);
322ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (mPendingCleanup.remove(animation.mViewHolder.itemView)) {
323ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            mCallback.clearView(mRecyclerView, animation.mViewHolder);
324d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                        }
325ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        select(animation.mViewHolder, animation.mActionState);
326ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        updateDxDy(event, mSelectedFlags, 0);
327ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
328ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
329ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
330ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mActivePointerId = ACTIVE_POINTER_ID_NONE;
331ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                select(null, ACTION_STATE_IDLE);
332ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {
333ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // in a non scroll orientation, if distance change is above threshold, we
334ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // can select the item
335ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final int index = event.findPointerIndex(mActivePointerId);
336ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (DEBUG) {
337ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    Log.d(TAG, "pointer index " + index);
338ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
339ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (index >= 0) {
340ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    checkSelectForSwipe(action, event, index);
341ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
342ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
343ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mVelocityTracker != null) {
344ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mVelocityTracker.addMovement(event);
345ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
346ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return mSelected != null;
347ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
348ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
349ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        @Override
350ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {
351ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mGestureDetector.onTouchEvent(event);
352ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (DEBUG) {
353ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                Log.d(TAG,
354ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        "on touch: x:" + mInitialTouchX + ",y:" + mInitialTouchY + ", :" + event);
355ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
356ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mVelocityTracker != null) {
357ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mVelocityTracker.addMovement(event);
358ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
359ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
360ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return;
361ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
362ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int action = MotionEventCompat.getActionMasked(event);
363ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int activePointerIndex = event.findPointerIndex(mActivePointerId);
364ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (activePointerIndex >= 0) {
365ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                checkSelectForSwipe(action, event, activePointerIndex);
366d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            }
367d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            ViewHolder viewHolder = mSelected;
368ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (viewHolder == null) {
369ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return;
370ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
371ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            switch (action) {
372ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                case MotionEvent.ACTION_MOVE: {
373ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    // Find the index of the active pointer and fetch its position
374ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (activePointerIndex >= 0) {
375ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        updateDxDy(event, mSelectedFlags, activePointerIndex);
376ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        moveIfNecessary(viewHolder);
377ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mRecyclerView.removeCallbacks(mScrollRunnable);
378ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mScrollRunnable.run();
379ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mRecyclerView.invalidate();
380ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
381ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    break;
382ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
383ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                case MotionEvent.ACTION_CANCEL:
384ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (mVelocityTracker != null) {
385ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mVelocityTracker.clear();
386ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
387ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    // fall through
388ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                case MotionEvent.ACTION_UP:
389ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    select(null, ACTION_STATE_IDLE);
390ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    mActivePointerId = ACTIVE_POINTER_ID_NONE;
391ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    break;
392ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                case MotionEvent.ACTION_POINTER_UP: {
393ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    final int pointerIndex = MotionEventCompat.getActionIndex(event);
394ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    final int pointerId = event.getPointerId(pointerIndex);
395ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (pointerId == mActivePointerId) {
396ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        // This was our active pointer going up. Choose a new
397ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        // active pointer and adjust accordingly.
398ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
399ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mActivePointerId = event.getPointerId(newPointerIndex);
400ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        updateDxDy(event, mSelectedFlags, pointerIndex);
401ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
402ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    break;
403ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
404ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
405d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent        }
406d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent
407ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        @Override
408ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
409ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (!disallowIntercept) {
410ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return;
411ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
412ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            select(null, ACTION_STATE_IDLE);
413ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
414ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    };
415ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
416ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
417ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Temporary rect instance that is used when we need to lookup Item decorations.
418ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
419ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private Rect mTmpRect;
420ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
421ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
422ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * When user started to drag scroll. Reset when we don't scroll
423ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
424ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private long mDragScrollStartTimeInMs;
425ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
426ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
427ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Creates an ItemTouchHelper that will work with the given Callback.
428ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <p>
429ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * You can attach ItemTouchHelper to a RecyclerView via
430ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link #attachToRecyclerView(RecyclerView)}. Upon attaching, it will add an item decoration,
431ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * an onItemTouchListener and a Child attach / detach listener to the RecyclerView.
432ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *
433ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * @param callback The Callback which controls the behavior of this touch helper.
434ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
435ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public ItemTouchHelper(Callback callback) {
436ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mCallback = callback;
437ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
438ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
439ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private static boolean hitTest(View child, float x, float y, float left, float top) {
440ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return x >= left &&
441d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                x <= left + child.getWidth() &&
442d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                y >= top &&
443d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                y <= top + child.getHeight();
444ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
445ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
446ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
447ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already
448ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * attached to a RecyclerView, it will first detach from the previous one. You can call this
449ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * method with {@code null} to detach it from the current RecyclerView.
450ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *
451ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * @param recyclerView The RecyclerView instance to which you want to add this helper or
452ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *                     {@code null} if you want to remove ItemTouchHelper from the current
453ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *                     RecyclerView.
454ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
455ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
456ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mRecyclerView == recyclerView) {
457ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return; // nothing to do
458ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
459ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mRecyclerView != null) {
460ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            destroyCallbacks();
461ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
462ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView = recyclerView;
463ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mRecyclerView != null) {
464ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final Resources resources = recyclerView.getResources();
465ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mSwipeEscapeVelocity = resources
466ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    .getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);
467ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mMaxSwipeVelocity = resources
468ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    .getDimension(R.dimen.item_touch_helper_swipe_escape_max_velocity);
469ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            setupCallbacks();
470ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
471ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
472ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
473ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private void setupCallbacks() {
474ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());
475ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mSlop = vc.getScaledTouchSlop();
476ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.addItemDecoration(this);
477ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);
478ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.addOnChildAttachStateChangeListener(this);
479ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        initGestureDetector();
480d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    }
481d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent
482d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    private void destroyCallbacks() {
483bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        mRecyclerView.removeItemDecoration(this);
484ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener);
485ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.removeOnChildAttachStateChangeListener(this);
486ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // clean all attached
487ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int recoverAnimSize = mRecoverAnimations.size();
488ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        for (int i = recoverAnimSize - 1; i >= 0; i--) {
489ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final RecoverAnimation recoverAnimation = mRecoverAnimations.get(0);
490ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mCallback.clearView(mRecyclerView, recoverAnimation.mViewHolder);
491bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        }
492ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecoverAnimations.clear();
493ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mOverdrawChild = null;
494bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        mOverdrawChildPosition = -1;
495bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        releaseVelocityTracker();
496bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent    }
497bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent
498bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent    private void initGestureDetector() {
499bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        if (mGestureDetector != null) {
500bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent            return;
501bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        }
502bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),
503bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent                new ItemTouchHelperGestureListener());
504ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
505ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
506ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private void getSelectedDxDy(float[] outPosition) {
507ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if ((mSelectedFlags & (LEFT | RIGHT)) != 0) {
508ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();
509ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        } else {
510ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            outPosition[0] = ViewCompat.getTranslationX(mSelected.itemView);
511ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
512bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent        if ((mSelectedFlags & (UP | DOWN)) != 0) {
513ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();
514ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        } else {
515ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            outPosition[1] = ViewCompat.getTranslationY(mSelected.itemView);
516ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
517ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
518ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
519ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    @Override
520ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
521ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        float dx = 0, dy = 0;
522ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mSelected != null) {
523ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            getSelectedDxDy(mTmpPosition);
524ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            dx = mTmpPosition[0];
525ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            dy = mTmpPosition[1];
526ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
527d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent        mCallback.onDrawOver(c, parent, mSelected,
528d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                mRecoverAnimations, mActionState, dx, dy);
529d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    }
530ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
531ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    @Override
532ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
533ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // we don't know if RV changed something so we should invalidate this index.
534ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mOverdrawChildPosition = -1;
535ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        float dx = 0, dy = 0;
536ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mSelected != null) {
537ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            getSelectedDxDy(mTmpPosition);
538ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            dx = mTmpPosition[0];
539ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            dy = mTmpPosition[1];
540ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
541ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mCallback.onDraw(c, parent, mSelected,
542ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mRecoverAnimations, mActionState, dx, dy);
543ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
544ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
545ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
546ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Starts dragging or swiping the given View. Call with null if you want to clear it.
547ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *
548ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * @param selected    The ViewHolder to drag or swipe. Can be null if you want to cancel the
549ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *                    current action
550ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * @param actionState The type of action
551ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
552ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    void select(ViewHolder selected, int actionState) {
553ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (selected == mSelected && actionState == mActionState) {
554ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return;
555ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
556ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mDragScrollStartTimeInMs = Long.MIN_VALUE;
557ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int prevActionState = mActionState;
558ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // prevent duplicate animations
559ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        endRecoverAnimation(selected, true);
560ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mActionState = actionState;
561ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (actionState == ACTION_STATE_DRAG) {
562ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            // we remove after animation is complete. this means we only elevate the last drag
563ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            // child but that should perform good enough as it is very hard to start dragging a
564ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            // new child before the previous one settles.
565ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mOverdrawChild = selected.itemView;
566ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            addChildDrawingOrderCallback();
567ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
568ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState))
569ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                - 1;
570ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        boolean preventLayout = false;
571ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
572ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mSelected != null) {
573ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final ViewHolder prevSelected = mSelected;
574ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (prevSelected.itemView.getParent() != null) {
575ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0
576ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        : swipeIfNecessary(prevSelected);
577ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                releaseVelocityTracker();
578ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // find where we should animate to
579ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float targetTranslateX, targetTranslateY;
580ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                int animationType;
581ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                switch (swipeDir) {
582ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    case LEFT:
583ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    case RIGHT:
584ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    case START:
585ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    case END:
586ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateY = 0;
587ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();
588ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        break;
589ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    case UP:
590ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    case DOWN:
591ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateX = 0;
592ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();
593ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        break;
594ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    default:
595ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateX = 0;
596ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateY = 0;
597ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
598ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (prevActionState == ACTION_STATE_DRAG) {
599ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    animationType = ANIMATION_TYPE_DRAG;
600ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                } else if (swipeDir > 0) {
601ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    animationType = ANIMATION_TYPE_SWIPE_SUCCESS;
602ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                } else {
603ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    animationType = ANIMATION_TYPE_SWIPE_CANCEL;
604ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
605ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                getSelectedDxDy(mTmpPosition);
606ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float currentTranslateX = mTmpPosition[0];
607ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float currentTranslateY = mTmpPosition[1];
608ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType,
609ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        prevActionState, currentTranslateX, currentTranslateY,
610ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateX, targetTranslateY) {
611ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    @Override
612ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    public void onAnimationEnd(ValueAnimatorCompat animation) {
613ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        super.onAnimationEnd(animation);
614ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (this.mOverridden) {
615ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            return;
616ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        }
617ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (swipeDir <= 0) {
618ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            // this is a drag or failed swipe. recover immediately
619d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                            mCallback.clearView(mRecyclerView, prevSelected);
620d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                            // full cleanup will happen on onDrawOver
621d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                        } else {
622d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                            // wait until remove animation is complete.
623ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            mPendingCleanup.add(prevSelected.itemView);
624ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            mIsPendingCleanup = true;
625ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            if (swipeDir > 0) {
626ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                                // Animation might be ended by other animators during a layout.
627ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                                // We defer callback to avoid editing adapter during a layout.
628ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                                postDispatchSwipe(this, swipeDir);
629ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            }
630ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        }
631ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        // removed from the list after it is drawn for the last time
632ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (mOverdrawChild == prevSelected.itemView) {
633ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
634ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        }
635ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
636ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                };
637ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,
638ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY);
639ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                rv.setDuration(duration);
640d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                mRecoverAnimations.add(rv);
641d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                rv.start();
642d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                preventLayout = true;
643ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else {
644ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
645ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mCallback.clearView(mRecyclerView, prevSelected);
646ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
647ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mSelected = null;
648ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
649ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (selected != null) {
650ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mSelectedFlags =
651ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    (mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask)
652ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            >> (mActionState * DIRECTION_FLAG_COUNT);
653ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mSelectedStartX = selected.itemView.getLeft();
654ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mSelectedStartY = selected.itemView.getTop();
655ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mSelected = selected;
656ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
657ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (actionState == ACTION_STATE_DRAG) {
658ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
659ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
660ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
661ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final ViewParent rvParent = mRecyclerView.getParent();
662ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (rvParent != null) {
663ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            rvParent.requestDisallowInterceptTouchEvent(mSelected != null);
664ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
665ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (!preventLayout) {
666ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();
667ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
668ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mCallback.onSelectedChanged(mSelected, mActionState);
669ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.invalidate();
670ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
671ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
672ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) {
673ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // wait until animations are complete.
674ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.post(new Runnable() {
675ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            @Override
676ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            public void run() {
677ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (mRecyclerView != null && mRecyclerView.isAttachedToWindow() &&
678ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        !anim.mOverridden &&
679d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                        anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {
680d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                    final RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator();
681d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent                    // if animator is running or we have other active recover animations, we try
682ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    // not to call onSwiped because DefaultItemAnimator is not good at merging
6837e1139c0377b6806942fb2a043737b3b9cf0ae91Eric Laurent                    // animations. Instead, we wait and batch.
684ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if ((animator == null || !animator.isRunning(null))
685ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            && !hasRunningRecoverAnim()) {
686ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mCallback.onSwiped(anim.mViewHolder, swipeDir);
687ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    } else {
688ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mRecyclerView.post(this);
689ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
690ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
691ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
692ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        });
693ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
694ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
695ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    boolean hasRunningRecoverAnim() {
696ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int size = mRecoverAnimations.size();
697ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        for (int i = 0; i < size; i++) {
698ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (!mRecoverAnimations.get(i).mEnded) {
699ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return true;
700ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
701d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent        }
702d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent        return false;
703d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent    }
704ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
705ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
706ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * If user drags the view to the edge, trigger a scroll if necessary.
707ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
708ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    boolean scrollIfNecessary() {
709ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mSelected == null) {
710ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mDragScrollStartTimeInMs = Long.MIN_VALUE;
711ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return false;
712ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
713ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final long now = System.currentTimeMillis();
714ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final long scrollDuration = mDragScrollStartTimeInMs
715ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                == Long.MIN_VALUE ? 0 : now - mDragScrollStartTimeInMs;
716ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
717ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mTmpRect == null) {
718ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mTmpRect = new Rect();
719ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
720ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        int scrollX = 0;
721ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        int scrollY = 0;
722ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        lm.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect);
723ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (lm.canScrollHorizontally()) {
724d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            int curX = (int) (mSelectedStartX + mDx);
725d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            final int leftDiff = curX - mTmpRect.left - mRecyclerView.getPaddingLeft();
726d0ebb538599cc25726b856e0f5deeb8215db8c92Eric Laurent            if (mDx < 0 && leftDiff < 0) {
727ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                scrollX = leftDiff;
728ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else if (mDx > 0) {
729ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final int rightDiff =
730ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        curX + mSelected.itemView.getWidth() + mTmpRect.right
731ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                                - (mRecyclerView.getWidth() - mRecyclerView.getPaddingRight());
732ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (rightDiff > 0) {
733ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    scrollX = rightDiff;
734ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
735ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
736ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
737ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (lm.canScrollVertically()) {
738ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            int curY = (int) (mSelectedStartY + mDy);
739ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int topDiff = curY - mTmpRect.top - mRecyclerView.getPaddingTop();
740ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mDy < 0 && topDiff < 0) {
741ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                scrollY = topDiff;
742ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else if (mDy > 0) {
743ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom -
744ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());
745ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (bottomDiff > 0) {
746ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    scrollY = bottomDiff;
747ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
748ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
749ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
750ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (scrollX != 0) {
751ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            scrollX = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,
752ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    mSelected.itemView.getWidth(), scrollX,
753ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    mRecyclerView.getWidth(), scrollDuration);
754ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
755ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (scrollY != 0) {
756ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            scrollY = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,
757ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    mSelected.itemView.getHeight(), scrollY,
758ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    mRecyclerView.getHeight(), scrollDuration);
759ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
760ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (scrollX != 0 || scrollY != 0) {
761ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mDragScrollStartTimeInMs == Long.MIN_VALUE) {
762ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mDragScrollStartTimeInMs = now;
763ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
764ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mRecyclerView.scrollBy(scrollX, scrollY);
765ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return true;
766ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
767ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mDragScrollStartTimeInMs = Long.MIN_VALUE;
7685baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        return false;
7695baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent    }
7705baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent
7715baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent    private List<ViewHolder> findSwapTargets(ViewHolder viewHolder) {
7725baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        if (mSwapTargets == null) {
7735baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            mSwapTargets = new ArrayList<ViewHolder>();
7745baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            mDistances = new ArrayList<Integer>();
7755baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        } else {
7765baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            mSwapTargets.clear();
7775baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            mDistances.clear();
7785baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        }
7795baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int margin = mCallback.getBoundingBoxMargin();
7805baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int left = Math.round(mSelectedStartX + mDx) - margin;
7815baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int top = Math.round(mSelectedStartY + mDy) - margin;
7825baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int right = left + viewHolder.itemView.getWidth() + 2 * margin;
7835baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int bottom = top + viewHolder.itemView.getHeight() + 2 * margin;
7845baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int centerX = (left + right) / 2;
7855baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int centerY = (top + bottom) / 2;
7865baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
7875baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        final int childCount = lm.getChildCount();
7885baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent        for (int i = 0; i < childCount; i++) {
7895baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            View other = lm.getChildAt(i);
7905baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            if (other == viewHolder.itemView) {
7915baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                continue;//myself!
7925baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            }
7935baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            if (other.getBottom() < top || other.getTop() > bottom
7945baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                    || other.getRight() < left || other.getLeft() > right) {
7955baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                continue;
7965baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            }
7975baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            final ViewHolder otherVh = mRecyclerView.getChildViewHolder(other);
7985baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent            if (mCallback.canDropOver(mRecyclerView, mSelected, otherVh)) {
7995baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                // find the index to add
8005baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                final int dx = Math.abs(centerX - (other.getLeft() + other.getRight()) / 2);
8015baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                final int dy = Math.abs(centerY - (other.getTop() + other.getBottom()) / 2);
8025baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                final int dist = dx * dx + dy * dy;
8035baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent
8045baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                int pos = 0;
8055baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                final int cnt = mSwapTargets.size();
8065baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                for (int j = 0; j < cnt; j++) {
8075baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent                    if (dist > mDistances.get(j)) {
808b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                        pos++;
809b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                    } else {
810b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                        break;
811b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                    }
812b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                }
813b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                mSwapTargets.add(pos, otherVh);
814b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                mDistances.add(pos, dist);
815b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            }
816b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
817b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        return mSwapTargets;
818b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    }
819b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
820b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    /**
821b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen     * Checks if we should swap w/ another view holder.
822b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen     */
823b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    void moveIfNecessary(ViewHolder viewHolder) {
824b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (mRecyclerView.isLayoutRequested()) {
825b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return;
826b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
827b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (mActionState != ACTION_STATE_DRAG) {
828b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return;
829b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
830b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
831b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        final float threshold = mCallback.getMoveThreshold(viewHolder);
832b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        final int x = (int) (mSelectedStartX + mDx);
833b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        final int y = (int) (mSelectedStartY + mDy);
834b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold
835b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                && Math.abs(x - viewHolder.itemView.getLeft())
836b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                < viewHolder.itemView.getWidth() * threshold) {
837b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return;
838b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
839b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        List<ViewHolder> swapTargets = findSwapTargets(viewHolder);
840b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (swapTargets.size() == 0) {
841b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return;
842b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
843b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        // may swap.
844b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        ViewHolder target = mCallback.chooseDropTarget(viewHolder, swapTargets, x, y);
845b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (target == null) {
846b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            mSwapTargets.clear();
847b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            mDistances.clear();
848b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return;
849b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
850b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        final int toPosition = target.getAdapterPosition();
851b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        final int fromPosition = viewHolder.getAdapterPosition();
852b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (mCallback.onMove(mRecyclerView, viewHolder, target)) {
853b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            // keep target visible
854b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            mCallback.onMoved(mRecyclerView, viewHolder, fromPosition,
855b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                    target, toPosition, x, y);
856b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
857b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    }
858b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
859b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    @Override
860b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    public void onChildViewAttachedToWindow(View view) {
861b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    }
862b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
863b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    @Override
864b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    public void onChildViewDetachedFromWindow(View view) {
865b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        removeChildDrawingOrderCallbackIfNecessary(view);
866b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        final ViewHolder holder = mRecyclerView.getChildViewHolder(view);
867b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (holder == null) {
868b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return;
869b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
870b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (mSelected != null && holder == mSelected) {
871b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            select(null, ACTION_STATE_IDLE);
872b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        } else {
873b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            endRecoverAnimation(holder, false); // this may push it into pending cleanup list.
874b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            if (mPendingCleanup.remove(holder.itemView)) {
875b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                mCallback.clearView(mRecyclerView, holder);
876b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            }
877b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
878b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    }
879b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
880b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    /**
881b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen     * Returns the animation type or 0 if cannot be found.
882b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen     */
883b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    int endRecoverAnimation(ViewHolder viewHolder, boolean override) {
884b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        final int recoverAnimSize = mRecoverAnimations.size();
885b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        for (int i = recoverAnimSize - 1; i >= 0; i--) {
886b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            final RecoverAnimation anim = mRecoverAnimations.get(i);
887b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            if (anim.mViewHolder == viewHolder) {
888b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                anim.mOverridden |= override;
889b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                if (!anim.mEnded) {
890b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                    anim.cancel();
891b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                }
892b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                mRecoverAnimations.remove(i);
893b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                return anim.mAnimationType;
894b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            }
895b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
896b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        return 0;
897b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    }
898b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
899b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    @Override
900b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
901b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            RecyclerView.State state) {
902b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        outRect.setEmpty();
903b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    }
904b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
905b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    void obtainVelocityTracker() {
906b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (mVelocityTracker != null) {
907b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            mVelocityTracker.recycle();
908b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        }
909b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        mVelocityTracker = VelocityTracker.obtain();
910b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    }
911b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
912b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen    private void releaseVelocityTracker() {
9130f11b51a57bc9062c4fe8af73747319cedabc5d6Glenn Kasten        if (mVelocityTracker != null) {
914ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mVelocityTracker.recycle();
915ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mVelocityTracker = null;
916ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
917ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
918ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
919ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private ViewHolder findSwipedView(MotionEvent motionEvent) {
920ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
921ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
922ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return null;
923ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
924ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);
925ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float dx = motionEvent.getX(pointerIndex) - mInitialTouchX;
926ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float dy = motionEvent.getY(pointerIndex) - mInitialTouchY;
927ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float absDx = Math.abs(dx);
928ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float absDy = Math.abs(dy);
929377b2ec9a2885f9b6405b07ba900a9e3f4349c38Kévin PETIT
930377b2ec9a2885f9b6405b07ba900a9e3f4349c38Kévin PETIT        if (absDx < mSlop && absDy < mSlop) {
931ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return null;
932ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
933ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (absDx > absDy && lm.canScrollHorizontally()) {
934ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return null;
935ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        } else if (absDy > absDx && lm.canScrollVertically()) {
936ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return null;
937ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
938ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        View child = findChildView(motionEvent);
939ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (child == null) {
940ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return null;
941ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
942ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return mRecyclerView.getChildViewHolder(child);
943ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
944ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
945ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
946ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Checks whether we should select a View for swiping.
947b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen     */
948ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) {
949b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (mSelected != null || action != MotionEvent.ACTION_MOVE
950b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen                || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) {
951ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return false;
952ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
953ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
954ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return false;
955ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
956ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final ViewHolder vh = findSwipedView(motionEvent);
957ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (vh == null) {
958ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return false;
959ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
960377b2ec9a2885f9b6405b07ba900a9e3f4349c38Kévin PETIT        final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh);
9611d6fa7af1288b550faabe4ec2cf98684236723dbNarayan Kamath
962ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK)
963ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE);
964ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
965b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        if (swipeFlags == 0) {
9661d6fa7af1288b550faabe4ec2cf98684236723dbNarayan Kamath            return false;
967377b2ec9a2885f9b6405b07ba900a9e3f4349c38Kévin PETIT        }
968ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
969ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // mDx and mDy are only set in allowed directions. We use custom x/y here instead of
970ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // updateDxDy to avoid swiping if user moves more in the other direction
971ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float x = motionEvent.getX(pointerIndex);
9721d6fa7af1288b550faabe4ec2cf98684236723dbNarayan Kamath        final float y = motionEvent.getY(pointerIndex);
973377b2ec9a2885f9b6405b07ba900a9e3f4349c38Kévin PETIT
974ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // Calculate the distance moved
975ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float dx = x - mInitialTouchX;
976ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float dy = y - mInitialTouchY;
977b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        // swipe target is chose w/o applying flags so it does not really check if swiping in that
978b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        // direction is allowed. This why here, we use mDx mDy to check slope value again.
979ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float absDx = Math.abs(dx);
980ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float absDy = Math.abs(dy);
981377b2ec9a2885f9b6405b07ba900a9e3f4349c38Kévin PETIT
982ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (absDx < mSlop && absDy < mSlop) {
983b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return false;
984ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
985ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (absDx > absDy) {
986ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (dx < 0 && (swipeFlags & LEFT) == 0) {
98701d3acba9de861cb2b718338e787cff3566fc5ecGlenn Kasten                return false;
988ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
989ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (dx > 0 && (swipeFlags & RIGHT) == 0) {
990ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return false;
991ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
992ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        } else {
993ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (dy < 0 && (swipeFlags & UP) == 0) {
994ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return false;
995ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
996ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (dy > 0 && (swipeFlags & DOWN) == 0) {
997ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return false;
998ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
999ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1000ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mDx = mDy = 0f;
1001ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mActivePointerId = motionEvent.getPointerId(0);
1002ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        select(vh, ACTION_STATE_SWIPE);
1003ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return true;
1004ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1005ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1006ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    View findChildView(MotionEvent event) {
1007ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // first check elevated views, if none, then call RV
1008ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float x = event.getX();
1009ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float y = event.getY();
1010ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mSelected != null) {
1011ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final View selectedView = mSelected.itemView;
1012ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy)) {
1013ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return selectedView;
1014ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1015ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1016ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
1017ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final RecoverAnimation anim = mRecoverAnimations.get(i);
1018ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final View view = anim.mViewHolder.itemView;
1019ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (hitTest(view, x, y, anim.mX, anim.mY)) {
1020ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return view;
1021e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten            }
1022e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten        }
1023ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return mRecyclerView.findChildViewUnder(x, y);
1024ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1025e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten
1026ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
1027ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Starts dragging the provided ViewHolder. By default, ItemTouchHelper starts a drag when a
1028e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten     * View is long pressed. You can disable that behavior by overriding
1029e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten     * {@link ItemTouchHelper.Callback#isLongPressDragEnabled()}.
1030ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <p>
1031ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * For this method to work:
1032ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <ul>
1033ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <li>The provided ViewHolder must be a child of the RecyclerView to which this
1034ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * ItemTouchHelper
1035ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * is attached.</li>
1036ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <li>{@link ItemTouchHelper.Callback} must have dragging enabled.</li>
1037ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <li>There must be a previous touch event that was reported to the ItemTouchHelper
1038ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener
1039ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * grabs previous events, this should work as expected.</li>
1040ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * </ul>
1041ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *
1042ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * For example, if you would like to let your user to be able to drag an Item by touching one
1043ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * of its descendants, you may implement it as follows:
1044ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <pre>
1045ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
1046e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten     *         public boolean onTouch(View v, MotionEvent event) {
1047e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten     *             if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
1048e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten     *                 mItemTouchHelper.startDrag(viewHolder);
1049e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten     *             }
1050e75da4004b2c814987aa2adf8a76190f92d99c65Glenn Kasten     *             return false;
1051ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *         }
1052ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *     });
1053ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * </pre>
1054ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <p>
1055ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *
1056ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * @param viewHolder The ViewHolder to start dragging. It must be a direct child of
1057ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *                   RecyclerView.
1058ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * @see ItemTouchHelper.Callback#isItemViewSwipeEnabled()
1059ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
1060ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public void startDrag(ViewHolder viewHolder) {
1061ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (!mCallback.hasDragFlag(mRecyclerView, viewHolder)) {
1062ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            Log.e(TAG, "Start drag has been called but swiping is not enabled");
1063ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return;
1064ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1065ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (viewHolder.itemView.getParent() != mRecyclerView) {
1066ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            Log.e(TAG, "Start drag has been called with a view holder which is not a child of "
1067ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    + "the RecyclerView which is controlled by this ItemTouchHelper.");
1068ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return;
1069ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1070ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        obtainVelocityTracker();
1071ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mDx = mDy = 0f;
1072ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        select(viewHolder, ACTION_STATE_DRAG);
1073ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1074ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1075ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
1076ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Starts swiping the provided ViewHolder. By default, ItemTouchHelper starts swiping a View
1077ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * when user swipes their finger (or mouse pointer) over the View. You can disable this
1078ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * behavior
1079ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * by overriding {@link ItemTouchHelper.Callback}
1080ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <p>
1081ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * For this method to work:
1082ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <ul>
1083813e2a74853bde19e37d878c596a044b3f299efcEric Laurent     * <li>The provided ViewHolder must be a child of the RecyclerView to which this
108459fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     * ItemTouchHelper is attached.</li>
108559fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     * <li>{@link ItemTouchHelper.Callback} must have swiping enabled.</li>
1086813e2a74853bde19e37d878c596a044b3f299efcEric Laurent     * <li>There must be a previous touch event that was reported to the ItemTouchHelper
108759fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener
108859fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     * grabs previous events, this should work as expected.</li>
1089813e2a74853bde19e37d878c596a044b3f299efcEric Laurent     * </ul>
109059fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     *
109159fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     * For example, if you would like to let your user to be able to swipe an Item by touching one
109259fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     * of its descendants, you may implement it as follows:
109359fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     * <pre>
109459fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
109559fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     *         public boolean onTouch(View v, MotionEvent event) {
109659fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     *             if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
109759fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent     *                 mItemTouchHelper.startSwipe(viewHolder);
1098813e2a74853bde19e37d878c596a044b3f299efcEric Laurent     *             }
1099813e2a74853bde19e37d878c596a044b3f299efcEric Laurent     *             return false;
1100ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *         }
1101ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *     });
1102ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * </pre>
1103ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *
1104ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * @param viewHolder The ViewHolder to start swiping. It must be a direct child of
1105ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     *                   RecyclerView.
1106ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
1107ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public void startSwipe(ViewHolder viewHolder) {
1108ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (!mCallback.hasSwipeFlag(mRecyclerView, viewHolder)) {
1109ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            Log.e(TAG, "Start swipe has been called but dragging is not enabled");
1110ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return;
1111ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1112ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (viewHolder.itemView.getParent() != mRecyclerView) {
1113ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            Log.e(TAG, "Start swipe has been called with a view holder which is not a child of "
1114ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    + "the RecyclerView controlled by this ItemTouchHelper.");
1115ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return;
1116ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1117ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        obtainVelocityTracker();
1118ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mDx = mDy = 0f;
1119ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        select(viewHolder, ACTION_STATE_SWIPE);
1120ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1121ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1122ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    RecoverAnimation findAnimation(MotionEvent event) {
1123ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mRecoverAnimations.isEmpty()) {
1124ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return null;
1125ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1126ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        View target = findChildView(event);
1127ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
112859fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent            final RecoverAnimation anim = mRecoverAnimations.get(i);
112959fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent            if (anim.mViewHolder.itemView == target) {
113059fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent                return anim;
113159fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent            }
113259fe010bcc072597852454a2ec53d7b0a2002a3bEric Laurent        }
1133ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return null;
1134ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1135ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1136ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) {
1137ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float x = ev.getX(pointerIndex);
1138ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final float y = ev.getY(pointerIndex);
1139ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1140ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        // Calculate the distance moved
1141ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mDx = x - mInitialTouchX;
1142ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mDy = y - mInitialTouchY;
1143ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if ((directionFlags & LEFT) == 0) {
1144ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mDx = Math.max(0, mDx);
1145ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1146ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if ((directionFlags & RIGHT) == 0) {
1147ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mDx = Math.min(0, mDx);
1148ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1149ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if ((directionFlags & UP) == 0) {
1150ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mDy = Math.max(0, mDy);
1151ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1152ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if ((directionFlags & DOWN) == 0) {
1153ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mDy = Math.min(0, mDy);
1154ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1155ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1156ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1157ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private int swipeIfNecessary(ViewHolder viewHolder) {
1158ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mActionState == ACTION_STATE_DRAG) {
1159ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return 0;
1160ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1161ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int originalMovementFlags = mCallback.getMovementFlags(mRecyclerView, viewHolder);
1162ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int absoluteMovementFlags = mCallback.convertToAbsoluteDirection(
1163ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                originalMovementFlags,
1164ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ViewCompat.getLayoutDirection(mRecyclerView));
1165021cf9634ab09c0753a40b7c9ef4ba603be5c3daEric Laurent        final int flags = (absoluteMovementFlags
1166021cf9634ab09c0753a40b7c9ef4ba603be5c3daEric Laurent                & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);
1167ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (flags == 0) {
1168ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return 0;
1169ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1170ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int originalFlags = (originalMovementFlags
1171ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);
1172ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        int swipeDir;
1173ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (Math.abs(mDx) > Math.abs(mDy)) {
1174ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {
1175ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // if swipe dir is not in original flags, it should be the relative direction
1176ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if ((originalFlags & swipeDir) == 0) {
1177ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    // convert to relative
1178ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    return Callback.convertToRelativeDirection(swipeDir,
1179ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            ViewCompat.getLayoutDirection(mRecyclerView));
1180ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1181ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return swipeDir;
1182ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1183ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {
1184ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return swipeDir;
1185ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1186ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        } else {
1187ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {
1188ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return swipeDir;
1189ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1190ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {
1191ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // if swipe dir is not in original flags, it should be the relative direction
1192ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if ((originalFlags & swipeDir) == 0) {
1193ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    // convert to relative
1194ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    return Callback.convertToRelativeDirection(swipeDir,
1195ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            ViewCompat.getLayoutDirection(mRecyclerView));
1196ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1197ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return swipeDir;
1198ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1199ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1200ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return 0;
1201ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1202ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1203ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private int checkHorizontalSwipe(ViewHolder viewHolder, int flags) {
1204ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if ((flags & (LEFT | RIGHT)) != 0) {
1205ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int dirFlag = mDx > 0 ? RIGHT : LEFT;
1206ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mVelocityTracker != null && mActivePointerId > -1) {
1207ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
1208ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
1209ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float xVelocity = VelocityTrackerCompat
1210ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        .getXVelocity(mVelocityTracker, mActivePointerId);
1211ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float yVelocity = VelocityTrackerCompat
1212ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        .getYVelocity(mVelocityTracker, mActivePointerId);
1213ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final int velDirFlag = xVelocity > 0f ? RIGHT : LEFT;
1214ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float absXVelocity = Math.abs(xVelocity);
1215ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag &&
1216ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&
1217ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        absXVelocity > Math.abs(yVelocity)) {
1218ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    return velDirFlag;
1219ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1220ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1221ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1222ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final float threshold = mRecyclerView.getWidth() * mCallback
1223ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    .getSwipeThreshold(viewHolder);
1224ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1225ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if ((flags & dirFlag) != 0 && Math.abs(mDx) > threshold) {
1226ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return dirFlag;
1227ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1228ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1229ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return 0;
1230ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1231ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1232ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private int checkVerticalSwipe(ViewHolder viewHolder, int flags) {
1233ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if ((flags & (UP | DOWN)) != 0) {
1234ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int dirFlag = mDy > 0 ? DOWN : UP;
1235ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mVelocityTracker != null && mActivePointerId > -1) {
1236ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
1237ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
1238ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float xVelocity = VelocityTrackerCompat
1239ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        .getXVelocity(mVelocityTracker, mActivePointerId);
1240ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float yVelocity = VelocityTrackerCompat
1241ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        .getYVelocity(mVelocityTracker, mActivePointerId);
1242ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final int velDirFlag = yVelocity > 0f ? DOWN : UP;
1243ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final float absYVelocity = Math.abs(yVelocity);
1244ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag &&
1245ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&
1246ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        absYVelocity > Math.abs(xVelocity)) {
1247ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    return velDirFlag;
1248ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1249ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1250ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1251ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final float threshold = mRecyclerView.getHeight() * mCallback
1252ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    .getSwipeThreshold(viewHolder);
1253ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if ((flags & dirFlag) != 0 && Math.abs(mDy) > threshold) {
1254ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return dirFlag;
1255ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1256ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1257ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        return 0;
1258ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1259ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1260ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    private void addChildDrawingOrderCallback() {
1261ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (Build.VERSION.SDK_INT >= 21) {
1262ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return;// we use elevation on Lollipop
1263ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1264ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (mChildDrawingOrderCallback == null) {
1265ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {
1266ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                @Override
1267ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                public int onGetChildDrawingOrder(int childCount, int i) {
1268ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (mOverdrawChild == null) {
1269ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        return i;
1270ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
1271ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    int childPosition = mOverdrawChildPosition;
1272ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (childPosition == -1) {
1273ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        childPosition = mRecyclerView.indexOfChild(mOverdrawChild);
1274ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        mOverdrawChildPosition = childPosition;
1275ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
1276ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (i == childCount - 1) {
1277ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        return childPosition;
1278ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
1279ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    return i < childPosition ? i : i + 1;
1280ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1281ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            };
1282ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1283ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback);
1284ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1285ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1286ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    void removeChildDrawingOrderCallbackIfNecessary(View view) {
1287ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        if (view == mOverdrawChild) {
1288ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            mOverdrawChild = null;
1289ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            // only remove if we've added
1290ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mChildDrawingOrderCallback != null) {
129101d3acba9de861cb2b718338e787cff3566fc5ecGlenn Kasten                mRecyclerView.setChildDrawingOrderCallback(null);
1292ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1293ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1294ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1295b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
1296ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
1297ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * An interface which can be implemented by LayoutManager for better integration with
1298b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen     * {@link ItemTouchHelper}.
1299b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen     */
1300ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public static interface ViewDropHandler {
1301ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1302ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1303ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Called by the {@link ItemTouchHelper} after a View is dropped over another View.
1304ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1305ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * A LayoutManager should implement this interface to get ready for the upcoming move
1306ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * operation.
1307ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1308ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * For example, LinearLayoutManager sets up a "scrollToPositionWithOffset" calls so that
1309ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * the View under drag will be used as an anchor View while calculating the next layout,
1310ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * making layout stay consistent.
1311ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1312ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param view   The View which is being dragged. It is very likely that user is still
1313ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *               dragging this View so there might be other
1314ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *               {@link #prepareForDrop(View, View, int, int)} after this one.
1315ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param target The target view which is being dropped on.
1316ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param x      The <code>left</code> offset of the View that is being dragged. This value
1317ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *               includes the movement caused by the user.
1318ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param y      The <code>top</code> offset of the View that is being dragged. This value
1319ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *               includes the movement caused by the user.
1320ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1321ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public void prepareForDrop(View view, View target, int x, int y);
1322ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    }
1323ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1324ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    /**
1325ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * This class is the contract between ItemTouchHelper and your application. It lets you control
1326ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * which touch behaviors are enabled per each ViewHolder and also receive callbacks when user
1327ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * performs these actions.
1328ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <p>
1329ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * To control which actions user can take on each view, you should override
1330ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link #getMovementFlags(RecyclerView, ViewHolder)} and return appropriate set
1331ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * of direction flags. ({@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link #END},
1332ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link #UP}, {@link #DOWN}). You can use
1333ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link #makeMovementFlags(int, int)} to easily construct it. Alternatively, you can use
1334ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link SimpleCallback}.
1335ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <p>
1336ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * If user drags an item, ItemTouchHelper will call
1337ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)
1338ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * onMove(recyclerView, dragged, target)}.
1339ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * Upon receiving this callback, you should move the item from the old position
1340ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * ({@code dragged.getAdapterPosition()}) to new position ({@code target.getAdapterPosition()})
1341ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * in your adapter and also call {@link RecyclerView.Adapter#notifyItemMoved(int, int)}.
1342ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * To control where a View can be dropped, you can override
1343ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link #canDropOver(RecyclerView, ViewHolder, ViewHolder)}. When a
1344ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * dragging View overlaps multiple other views, Callback chooses the closest View with which
1345ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * dragged View might have changed positions. Although this approach works for many use cases,
1346ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * if you have a custom LayoutManager, you can override
1347ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)} to select a
1348ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * custom drop target.
1349ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * <p>
1350ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls
1351ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * {@link #onSwiped(ViewHolder, int)}. At this point, you should update your
1352ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     * adapter (e.g. remove the item) and call related Adapter#notify event.
1353ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent     */
1354ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    @SuppressWarnings("UnusedParameters")
1355ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent    public abstract static class Callback {
1356ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1357ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200;
1358ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1359ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250;
1360ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1361ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        static final int RELATIVE_DIR_FLAGS = START | END |
1362ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ((START | END) << DIRECTION_FLAG_COUNT) |
1363ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ((START | END) << (2 * DIRECTION_FLAG_COUNT));
1364ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1365ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        private static final ItemTouchUIUtil sUICallback;
1366ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1367ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        private static final int ABS_HORIZONTAL_DIR_FLAGS = LEFT | RIGHT |
1368ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ((LEFT | RIGHT) << DIRECTION_FLAG_COUNT) |
1369ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ((LEFT | RIGHT) << (2 * DIRECTION_FLAG_COUNT));
1370ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1371ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        private static final Interpolator sDragScrollInterpolator = new Interpolator() {
1372ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            @Override
1373ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            public float getInterpolation(float t) {
1374ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return t * t * t * t * t;
1375ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1376ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        };
1377ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1378ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        private static final Interpolator sDragViewScrollCapInterpolator = new Interpolator() {
1379ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            @Override
1380ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            public float getInterpolation(float t) {
1381ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                t -= 1.0f;
1382ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return t * t * t * t * t + 1.0f;
1383ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1384ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        };
1385ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1386ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1387ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Drag scroll speed keeps accelerating until this many milliseconds before being capped.
1388ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1389ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
1390bfb1b832079bbb9426f72f3863199a54aefd02daEric Laurent
1391ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        private int mCachedMaxScrollSpeed = -1;
1392ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1393ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        static {
1394ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (Build.VERSION.SDK_INT >= 21) {
1395ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                sUICallback = new ItemTouchUIUtilImpl.Lollipop();
1396ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else if (Build.VERSION.SDK_INT >= 11) {
1397ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                sUICallback = new ItemTouchUIUtilImpl.Honeycomb();
1398ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else {
1399ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                sUICallback = new ItemTouchUIUtilImpl.Gingerbread();
1400ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1401ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1402ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1403fed6292af65a0b97b583ecbd3c232b3811a3f37bJean-Michel Trivi        /**
1404fed6292af65a0b97b583ecbd3c232b3811a3f37bJean-Michel Trivi         * Returns the {@link ItemTouchUIUtil} that is used by the {@link Callback} class for
1405fed6292af65a0b97b583ecbd3c232b3811a3f37bJean-Michel Trivi         * visual
1406fed6292af65a0b97b583ecbd3c232b3811a3f37bJean-Michel Trivi         * changes on Views in response to user interactions. {@link ItemTouchUIUtil} has different
1407ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * implementations for different platform versions.
1408ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1409ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * By default, {@link Callback} applies these changes on
1410ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * {@link RecyclerView.ViewHolder#itemView}.
1411ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1412ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * For example, if you have a use case where you only want the text to move when user
1413ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * swipes over the view, you can do the following:
1414ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <pre>
1415ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){
1416ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *         getDefaultUIUtil().clearView(((ItemTouchViewHolder) viewHolder).textView);
1417ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     }
1418ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
1419ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *         if (viewHolder != null){
1420ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *             getDefaultUIUtil().onSelected(((ItemTouchViewHolder) viewHolder).textView);
1421ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *         }
1422ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     }
1423ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     public void onChildDraw(Canvas c, RecyclerView recyclerView,
1424ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
1425ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *             boolean isCurrentlyActive) {
1426ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *         getDefaultUIUtil().onDraw(c, recyclerView,
1427ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,
1428ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                 actionState, isCurrentlyActive);
1429ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *         return true;
1430ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     }
1431ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
1432ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
1433ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *             boolean isCurrentlyActive) {
1434ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *         getDefaultUIUtil().onDrawOver(c, recyclerView,
1435ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,
1436ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                 actionState, isCurrentlyActive);
1437ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *         return true;
1438ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *     }
1439ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * </pre>
1440ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1441ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return The {@link ItemTouchUIUtil} instance that is used by the {@link Callback}
1442ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1443ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public static ItemTouchUIUtil getDefaultUIUtil() {
1444ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return sUICallback;
1445ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1446ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1447ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1448ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Replaces a movement direction with its relative version by taking layout direction into
1449ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * account.
1450ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1451ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param flags           The flag value that include any number of movement flags.
1452ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param layoutDirection The layout direction of the View. Can be obtained from
1453ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                        {@link ViewCompat#getLayoutDirection(android.view.View)}.
1454ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return Updated flags which uses relative flags ({@link #START}, {@link #END}) instead
1455ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * of {@link #LEFT}, {@link #RIGHT}.
1456ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #convertToAbsoluteDirection(int, int)
1457ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1458ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public static int convertToRelativeDirection(int flags, int layoutDirection) {
1459ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            int masked = flags & ABS_HORIZONTAL_DIR_FLAGS;
1460ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (masked == 0) {
1461ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return flags;// does not have any abs flags, good.
1462ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1463ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            flags &= ~masked; //remove left / right.
1464ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
1465ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // no change. just OR with 2 bits shifted mask and return
1466ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                flags |= masked << 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.
1467ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return flags;
1468ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else {
1469ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // add RIGHT flag as START
1470ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                flags |= ((masked << 1) & ~ABS_HORIZONTAL_DIR_FLAGS);
1471ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // first clean RIGHT bit then add LEFT flag as END
1472ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                flags |= ((masked << 1) & ABS_HORIZONTAL_DIR_FLAGS) << 2;
1473ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1474ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return flags;
1475ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1476ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1477ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1478ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Convenience method to create movement flags.
1479ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1480ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * For instance, if you want to let your items be drag & dropped vertically and swiped
1481ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * left to be dismissed, you can call this method with:
1482ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <code>makeMovementFlags(UP | DOWN, LEFT);</code>
1483ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1484ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param dragFlags  The directions in which the item can be dragged.
1485ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param swipeFlags The directions in which the item can be swiped.
1486ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return Returns an integer composed of the given drag and swipe flags.
1487ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1488ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public static int makeMovementFlags(int dragFlags, int swipeFlags) {
1489ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags) |
1490ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    makeFlag(ACTION_STATE_SWIPE, swipeFlags) | makeFlag(ACTION_STATE_DRAG,
1491ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    dragFlags);
1492ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1493ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1494ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1495ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Shifts the given direction flags to the offset of the given action state.
1496ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1497ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param actionState The action state you want to get flags in. Should be one of
1498ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    {@link #ACTION_STATE_IDLE}, {@link #ACTION_STATE_SWIPE} or
1499ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    {@link #ACTION_STATE_DRAG}.
1500ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param directions  The direction flags. Can be composed from {@link #UP}, {@link #DOWN},
1501ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    {@link #RIGHT}, {@link #LEFT} {@link #START} and {@link #END}.
1502ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return And integer that represents the given directions in the provided actionState.
1503ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1504ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public static int makeFlag(int actionState, int directions) {
1505ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return directions << (actionState * DIRECTION_FLAG_COUNT);
1506ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1507ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1508ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1509ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Should return a composite flag which defines the enabled move directions in each state
1510ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * (idle, swiping, dragging).
1511ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1512ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Instead of composing this flag manually, you can use {@link #makeMovementFlags(int,
1513ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * int)}
1514ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * or {@link #makeFlag(int, int)}.
1515ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1516ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * This flag is composed of 3 sets of 8 bits, where first 8 bits are for IDLE state, next
1517ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * 8 bits are for SWIPE state and third 8 bits are for DRAG state.
1518ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Each 8 bit sections can be constructed by simply OR'ing direction flags defined in
1519ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * {@link ItemTouchHelper}.
1520ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1521ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to
1522ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * swipe by swiping RIGHT, you can return:
1523ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <pre>
1524ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *      makeFlag(ACTION_STATE_IDLE, RIGHT) | makeFlag(ACTION_STATE_SWIPE, LEFT | RIGHT);
1525ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * </pre>
1526ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * This means, allow right movement while IDLE and allow right and left movement while
1527ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * swiping.
1528ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1529ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached.
1530ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param viewHolder   The ViewHolder for which the movement information is necessary.
1531ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return flags specifying which movements are allowed on this ViewHolder.
1532ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #makeMovementFlags(int, int)
1533ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #makeFlag(int, int)
1534ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1535ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public abstract int getMovementFlags(RecyclerView recyclerView,
1536ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ViewHolder viewHolder);
1537ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1538ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1539ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Converts a given set of flags to absolution direction which means {@link #START} and
1540ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * {@link #END} are replaced with {@link #LEFT} and {@link #RIGHT} depending on the layout
1541ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * direction.
1542ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1543ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param flags           The flag value that include any number of movement flags.
1544ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param layoutDirection The layout direction of the RecyclerView.
1545ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return Updated flags which includes only absolute direction values.
1546ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1547ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public int convertToAbsoluteDirection(int flags, int layoutDirection) {
1548ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            int masked = flags & RELATIVE_DIR_FLAGS;
1549ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (masked == 0) {
1550ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return flags;// does not have any relative flags, good.
1551ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1552ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            flags &= ~masked; //remove start / end
1553ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
1554ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // no change. just OR with 2 bits shifted mask and return
1555ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                flags |= masked >> 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.
1556ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                return flags;
1557ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            } else {
1558ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // add START flag as RIGHT
1559ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                flags |= ((masked >> 1) & ~RELATIVE_DIR_FLAGS);
1560ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                // first clean start bit then add END flag as LEFT
1561ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                flags |= ((masked >> 1) & RELATIVE_DIR_FLAGS) >> 2;
1562ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1563ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return flags;
1564ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1565ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1566ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        final int getAbsoluteMovementFlags(RecyclerView recyclerView,
1567ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ViewHolder viewHolder) {
1568ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int flags = getMovementFlags(recyclerView, viewHolder);
1569ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return convertToAbsoluteDirection(flags, ViewCompat.getLayoutDirection(recyclerView));
1570ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1571ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1572ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        boolean hasDragFlag(RecyclerView recyclerView, ViewHolder viewHolder) {
1573ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);
1574ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return (flags & ACTION_MODE_DRAG_MASK) != 0;
1575ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1576ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1577ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        boolean hasSwipeFlag(RecyclerView recyclerView,
1578ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ViewHolder viewHolder) {
1579ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);
1580ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return (flags & ACTION_MODE_SWIPE_MASK) != 0;
1581ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1582ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1583ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1584ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Return true if the current ViewHolder can be dropped over the the target ViewHolder.
1585ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1586ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * This method is used when selecting drop target for the dragged View. After Views are
1587ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * eliminated either via bounds check or via this method, resulting set of views will be
1588ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * passed to {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)}.
1589ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1590ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Default implementation returns true.
1591ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1592ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.
1593ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param current      The ViewHolder that user is dragging.
1594ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param target       The ViewHolder which is below the dragged ViewHolder.
1595ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return True if the dragged ViewHolder can be replaced with the target ViewHolder, false
1596ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * otherwise.
1597ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1598ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
1599ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ViewHolder target) {
1600ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return true;
1601ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1602ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1603ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1604ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Called when ItemTouchHelper wants to move the dragged item from its old position to
1605ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * the new position.
1606ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1607ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * If this method returns true, ItemTouchHelper assumes {@code viewHolder} has been moved
1608ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * to the adapter position of {@code target} ViewHolder
1609ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * ({@link ViewHolder#getAdapterPosition()
1610ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * ViewHolder#getAdapterPosition()}).
1611ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1612ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * If you don't support drag & drop, this method will never be called.
1613ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1614ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.
1615ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param viewHolder   The ViewHolder which is being dragged by the user.
1616ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param target       The ViewHolder over which the currently active item is being
1617ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                     dragged.
1618ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return True if the {@code viewHolder} has been moved to the adapter position of
1619ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * {@code target}.
1620ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #onMoved(RecyclerView, ViewHolder, int, ViewHolder, int, int, int)
1621ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1622ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public abstract boolean onMove(RecyclerView recyclerView,
1623ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                ViewHolder viewHolder, ViewHolder target);
1624ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1625ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1626ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Returns whether ItemTouchHelper should start a drag and drop operation if an item is
1627ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * long pressed.
1628ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1629ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Default value returns true but you may want to disable this if you want to start
1630ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * dragging on a custom view touch using {@link #startDrag(ViewHolder)}.
1631ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1632ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return True if ItemTouchHelper should start dragging an item when it is long pressed,
1633ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * false otherwise. Default value is <code>true</code>.
1634ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #startDrag(ViewHolder)
1635ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1636ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public boolean isLongPressDragEnabled() {
1637ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return true;
1638ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1639ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1640ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1641ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Returns whether ItemTouchHelper should start a swipe operation if a pointer is swiped
1642ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * over the View.
1643ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1644ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Default value returns true but you may want to disable this if you want to start
1645ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * swiping on a custom view touch using {@link #startSwipe(ViewHolder)}.
1646ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1647ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return True if ItemTouchHelper should start swiping an item when user swipes a pointer
1648ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * over the View, false otherwise. Default value is <code>true</code>.
1649ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #startSwipe(ViewHolder)
1650ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1651ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public boolean isItemViewSwipeEnabled() {
1652ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return true;
1653ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1654ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1655ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1656ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * When finding views under a dragged view, by default, ItemTouchHelper searches for views
1657ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * that overlap with the dragged View. By overriding this method, you can extend or shrink
1658ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * the search box.
1659ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1660ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return The extra margin to be added to the hit box of the dragged View.
1661ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1662ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public int getBoundingBoxMargin() {
1663ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return 0;
1664ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1665ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1666ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1667ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Returns the fraction that the user should move the View to be considered as swiped.
1668ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * The fraction is calculated with respect to RecyclerView's bounds.
1669ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1670ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Default value is .5f, which means, to swipe a View, user must move the View at least
1671ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * half of RecyclerView's width or height, depending on the swipe direction.
1672ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1673ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param viewHolder The ViewHolder that is being dragged.
1674ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return A float value that denotes the fraction of the View size. Default value
1675ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * is .5f .
1676ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1677ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public float getSwipeThreshold(ViewHolder viewHolder) {
1678ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return .5f;
1679ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1680b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen
1681b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen        /**
1682ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Returns the fraction that the user should move the View to be considered as it is
1683ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views
1684b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * below it for a possible drop.
1685b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         *
1686b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * @param viewHolder The ViewHolder that is being dragged.
1687b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * @return A float value that denotes the fraction of the View size. Default value is
1688b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * .5f .
1689b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         */
1690ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public float getMoveThreshold(ViewHolder viewHolder) {
1691b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen            return .5f;
16921d6fa7af1288b550faabe4ec2cf98684236723dbNarayan Kamath        }
16931d6fa7af1288b550faabe4ec2cf98684236723dbNarayan Kamath
16941d6fa7af1288b550faabe4ec2cf98684236723dbNarayan Kamath        /**
1695b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * Defines the minimum velocity which will be considered as a swipe action by the user.
1696b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * <p>
1697b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * You can increase this value to make it harder to swipe or decrease it to make it easier.
1698b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * Keep in mind that ItemTouchHelper also checks the perpendicular velocity and makes sure
1699b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * current direction velocity is larger then the perpendicular one. Otherwise, user's
1700b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * movement is ambiguous. You can change the threshold by overriding
1701b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * {@link #getSwipeVelocityThreshold(float)}.
1702b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * <p>
1703b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * The velocity is calculated in pixels per second.
1704ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1705ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * The default framework value is passed as a parameter so that you can modify it with a
1706b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * multiplier.
1707b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         *
1708b220884bf3129253cc5bc8d030bc475411ea4911Marco Nelissen         * @param defaultValue The default value (in pixels per second) used by the
1709ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                     ItemTouchHelper.
1710ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return The minimum swipe velocity. The default implementation returns the
1711ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <code>defaultValue</code> parameter.
1712ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #getSwipeVelocityThreshold(float)
1713ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #getSwipeThreshold(ViewHolder)
1714ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1715ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public float getSwipeEscapeVelocity(float defaultValue) {
1716ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return defaultValue;
1717ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1718ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1719ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1720ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Defines the maximum velocity ItemTouchHelper will ever calculate for pointer movements.
1721ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1722ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * To consider a movement as swipe, ItemTouchHelper requires it to be larger than the
1723ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * perpendicular movement. If both directions reach to the max threshold, none of them will
1724ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * be considered as a swipe because it is usually an indication that user rather tried to
1725ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * scroll then swipe.
1726ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1727ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * The velocity is calculated in pixels per second.
1728ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1729ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * You can customize this behavior by changing this method. If you increase the value, it
1730ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * will be easier for the user to swipe diagonally and if you decrease the value, user will
1731ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * need to make a rather straight finger movement to trigger a swipe.
1732ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1733ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param defaultValue The default value(in pixels per second) used by the ItemTouchHelper.
1734ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return The velocity cap for pointer movements. The default implementation returns the
1735ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <code>defaultValue</code> parameter.
1736ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #getSwipeEscapeVelocity(float)
1737ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1738ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public float getSwipeVelocityThreshold(float defaultValue) {
1739ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return defaultValue;
1740ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1741ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1742ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1743ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Called by ItemTouchHelper to select a drop target from the list of ViewHolders that
1744ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * are under the dragged View.
1745ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1746ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Default implementation filters the View with which dragged item have changed position
1747ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * in the drag direction. For instance, if the view is dragged UP, it compares the
1748ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <code>view.getTop()</code> of the two views before and after drag started. If that value
1749ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * is different, the target view passes the filter.
1750ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1751ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Among these Views which pass the test, the one closest to the dragged view is chosen.
1752ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1753ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * This method is called on the main thread every time user moves the View. If you want to
1754ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * override it, make sure it does not do any expensive operations.
1755ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1756ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param selected    The ViewHolder being dragged by the user.
1757ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param dropTargets The list of ViewHolder that are under the dragged View and
1758ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    candidate as a drop.
1759ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param curX        The updated left value of the dragged View after drag translations
1760ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    are applied. This value does not include margins added by
1761ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    {@link RecyclerView.ItemDecoration}s.
1762ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param curY        The updated top value of the dragged View after drag translations
1763ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    are applied. This value does not include margins added by
1764ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    {@link RecyclerView.ItemDecoration}s.
1765ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @return A ViewHolder to whose position the dragged ViewHolder should be
1766ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * moved to.
1767ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1768ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public ViewHolder chooseDropTarget(ViewHolder selected,
1769ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                List<ViewHolder> dropTargets, int curX, int curY) {
1770ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            int right = curX + selected.itemView.getWidth();
1771ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            int bottom = curY + selected.itemView.getHeight();
1772ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            ViewHolder winner = null;
1773ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            int winnerScore = -1;
1774ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int dx = curX - selected.itemView.getLeft();
1775ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int dy = curY - selected.itemView.getTop();
1776ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            final int targetsSize = dropTargets.size();
1777ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            for (int i = 0; i < targetsSize; i++) {
1778ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                final ViewHolder target = dropTargets.get(i);
1779ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (dx > 0) {
1780ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    int diff = target.itemView.getRight() - right;
1781ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) {
1782ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        final int score = Math.abs(diff);
1783ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (score > winnerScore) {
1784ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winnerScore = score;
1785ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winner = target;
1786ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        }
1787ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
1788ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1789ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (dx < 0) {
1790ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    int diff = target.itemView.getLeft() - curX;
1791ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) {
1792ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        final int score = Math.abs(diff);
1793ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (score > winnerScore) {
1794ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winnerScore = score;
1795ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winner = target;
1796ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        }
1797ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
1798ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1799ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (dy < 0) {
1800ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    int diff = target.itemView.getTop() - curY;
1801ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (diff > 0 && target.itemView.getTop() < selected.itemView.getTop()) {
1802ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        final int score = Math.abs(diff);
1803ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (score > winnerScore) {
1804ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winnerScore = score;
1805ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winner = target;
1806ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        }
1807ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
1808ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1809ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1810ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                if (dy > 0) {
1811ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    int diff = target.itemView.getBottom() - bottom;
1812ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    if (diff < 0 && target.itemView.getBottom() > selected.itemView.getBottom()) {
1813ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        final int score = Math.abs(diff);
1814ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        if (score > winnerScore) {
1815ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winnerScore = score;
1816ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                            winner = target;
1817ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        }
1818ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                    }
1819ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                }
1820ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1821ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return winner;
1822ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1823ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1824ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1825ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Called when a ViewHolder is swiped by the user.
1826ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1827ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * If you are returning relative directions ({@link #START} , {@link #END}) from the
1828ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * {@link #getMovementFlags(RecyclerView, ViewHolder)} method, this method
1829ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * will also use relative directions. Otherwise, it will use absolute directions.
1830ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1831ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * If you don't support swiping, this method will never be called.
1832ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1833ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * ItemTouchHelper will keep a reference to the View until it is detached from
1834ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * RecyclerView.
1835ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * As soon as it is detached, ItemTouchHelper will call
1836ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * {@link #clearView(RecyclerView, ViewHolder)}.
1837ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1838ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param viewHolder The ViewHolder which has been swiped by the user.
1839ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param direction  The direction to which the ViewHolder is swiped. It is one of
1840ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                   {@link #UP}, {@link #DOWN},
1841ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                   {@link #LEFT} or {@link #RIGHT}. If your
1842ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                   {@link #getMovementFlags(RecyclerView, ViewHolder)}
1843ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                   method
1844ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                   returned relative flags instead of {@link #LEFT} / {@link #RIGHT};
1845ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                   `direction` will be relative as well. ({@link #START} or {@link
1846ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                   #END}).
1847ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1848ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public abstract void onSwiped(ViewHolder viewHolder, int direction);
1849ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1850ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1851ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed.
1852ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p/>
1853ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * If you override this method, you should call super.
1854ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1855ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param viewHolder  The new ViewHolder that is being swiped or dragged. Might be null if
1856ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    it is cleared.
1857ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @param actionState One of {@link ItemTouchHelper#ACTION_STATE_IDLE},
1858ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    {@link ItemTouchHelper#ACTION_STATE_SWIPE} or
1859ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                    {@link ItemTouchHelper#ACTION_STATE_DRAG}.
1860ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * @see #clearView(RecyclerView, RecyclerView.ViewHolder)
1861ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         */
1862ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
1863ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (viewHolder != null) {
1864ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                sUICallback.onSelected(viewHolder.itemView);
1865ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1866ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1867ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1868ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        private int getMaxDragScroll(RecyclerView recyclerView) {
1869ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            if (mCachedMaxScrollSpeed == -1) {
1870ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                mCachedMaxScrollSpeed = recyclerView.getResources().getDimensionPixelSize(
1871ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent                        R.dimen.item_touch_helper_max_drag_scroll_per_frame);
1872ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            }
1873ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent            return mCachedMaxScrollSpeed;
1874ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        }
1875ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent
1876ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent        /**
1877ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Called when {@link #onMove(RecyclerView, ViewHolder, ViewHolder)} returns true.
1878ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1879ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * ItemTouchHelper does not create an extra Bitmap or View while dragging, instead, it
1880ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * modifies the existing View. Because of this reason, it is important that the View is
1881ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * still part of the layout after it is moved. This may not work as intended when swapped
1882ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Views are close to RecyclerView bounds or there are gaps between them (e.g. other Views
1883ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * which were not eligible for dropping over).
1884ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1885ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * This method is responsible to give necessary hint to the LayoutManager so that it will
1886ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * keep the View in visible area. For example, for LinearLayoutManager, this is as simple
1887ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * as calling {@link LinearLayoutManager#scrollToPositionWithOffset(int, int)}.
1888ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *
1889ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * Default implementation calls {@link RecyclerView#scrollToPosition(int)} if the View's
1890ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * new position is likely to be out of bounds.
1891ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * <p>
1892ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * It is important to ensure the ViewHolder will stay visible as otherwise, it might be
1893ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         * removed by the LayoutManager if the move causes the View to go out of bounds. In that
18945baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent         * case, drag will end prematurely.
1895813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         *
1896813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         * @param recyclerView The RecyclerView controlled by the ItemTouchHelper.
1897813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         * @param viewHolder   The ViewHolder under user's control.
1898813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         * @param fromPos      The previous adapter position of the dragged item (before it was
18995baf2af52cd186633b7173196c1e4a4cd3435f22Eric Laurent         *                     moved).
1900813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         * @param target       The ViewHolder on which the currently active item has been dropped.
1901813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         * @param toPos        The new adapter position of the dragged item.
1902813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         * @param x            The updated left value of the dragged View after drag translations
1903813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         *                     are applied. This value does not include margins added by
1904813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         *                     {@link RecyclerView.ItemDecoration}s.
1905813e2a74853bde19e37d878c596a044b3f299efcEric Laurent         * @param y            The updated top value of the dragged View after drag translations
1906ca7cc8273ffd88b9b89655808ee7e3df74162b83Eric Laurent         *                     are applied. This value does not include margins added by
1907         *                     {@link RecyclerView.ItemDecoration}s.
1908         */
1909        public void onMoved(final RecyclerView recyclerView,
1910                final ViewHolder viewHolder, int fromPos, final ViewHolder target, int toPos, int x,
1911                int y) {
1912            final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
1913            if (layoutManager instanceof ViewDropHandler) {
1914                ((ViewDropHandler) layoutManager).prepareForDrop(viewHolder.itemView,
1915                        target.itemView, x, y);
1916                return;
1917            }
1918
1919            // if layout manager cannot handle it, do some guesswork
1920            if (layoutManager.canScrollHorizontally()) {
1921                final int minLeft = layoutManager.getDecoratedLeft(target.itemView);
1922                if (minLeft <= recyclerView.getPaddingLeft()) {
1923                    recyclerView.scrollToPosition(toPos);
1924                }
1925                final int maxRight = layoutManager.getDecoratedRight(target.itemView);
1926                if (maxRight >= recyclerView.getWidth() - recyclerView.getPaddingRight()) {
1927                    recyclerView.scrollToPosition(toPos);
1928                }
1929            }
1930
1931            if (layoutManager.canScrollVertically()) {
1932                final int minTop = layoutManager.getDecoratedTop(target.itemView);
1933                if (minTop <= recyclerView.getPaddingTop()) {
1934                    recyclerView.scrollToPosition(toPos);
1935                }
1936                final int maxBottom = layoutManager.getDecoratedBottom(target.itemView);
1937                if (maxBottom >= recyclerView.getHeight() - recyclerView.getPaddingBottom()) {
1938                    recyclerView.scrollToPosition(toPos);
1939                }
1940            }
1941        }
1942
1943        void onDraw(Canvas c, RecyclerView parent, ViewHolder selected,
1944                List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,
1945                int actionState, float dX, float dY) {
1946            final int recoverAnimSize = recoverAnimationList.size();
1947            for (int i = 0; i < recoverAnimSize; i++) {
1948                final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);
1949                anim.update();
1950                final int count = c.save();
1951                onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,
1952                        false);
1953                c.restoreToCount(count);
1954            }
1955            if (selected != null) {
1956                final int count = c.save();
1957                onChildDraw(c, parent, selected, dX, dY, actionState, true);
1958                c.restoreToCount(count);
1959            }
1960        }
1961
1962        void onDrawOver(Canvas c, RecyclerView parent, ViewHolder selected,
1963                List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,
1964                int actionState, float dX, float dY) {
1965            final int recoverAnimSize = recoverAnimationList.size();
1966            for (int i = 0; i < recoverAnimSize; i++) {
1967                final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);
1968                final int count = c.save();
1969                onChildDrawOver(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,
1970                        false);
1971                c.restoreToCount(count);
1972            }
1973            if (selected != null) {
1974                final int count = c.save();
1975                onChildDrawOver(c, parent, selected, dX, dY, actionState, true);
1976                c.restoreToCount(count);
1977            }
1978            boolean hasRunningAnimation = false;
1979            for (int i = recoverAnimSize - 1; i >= 0; i--) {
1980                final RecoverAnimation anim = recoverAnimationList.get(i);
1981                if (anim.mEnded && !anim.mIsPendingCleanup) {
1982                    recoverAnimationList.remove(i);
1983                } else if (!anim.mEnded) {
1984                    hasRunningAnimation = true;
1985                }
1986            }
1987            if (hasRunningAnimation) {
1988                parent.invalidate();
1989            }
1990        }
1991
1992        /**
1993         * Called by the ItemTouchHelper when the user interaction with an element is over and it
1994         * also completed its animation.
1995         * <p>
1996         * This is a good place to clear all changes on the View that was done in
1997         * {@link #onSelectedChanged(RecyclerView.ViewHolder, int)},
1998         * {@link #onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,
1999         * boolean)} or
2000         * {@link #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean)}.
2001         *
2002         * @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper.
2003         * @param viewHolder   The View that was interacted by the user.
2004         */
2005        public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
2006            sUICallback.clearView(viewHolder.itemView);
2007        }
2008
2009        /**
2010         * Called by ItemTouchHelper on RecyclerView's onDraw callback.
2011         * <p>
2012         * If you would like to customize how your View's respond to user interactions, this is
2013         * a good place to override.
2014         * <p>
2015         * Default implementation translates the child by the given <code>dX</code>,
2016         * <code>dY</code>.
2017         * ItemTouchHelper also takes care of drawing the child after other children if it is being
2018         * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this
2019         * is
2020         * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L
2021         * and after, it changes View's elevation value to be greater than all other children.)
2022         *
2023         * @param c                 The canvas which RecyclerView is drawing its children
2024         * @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to
2025         * @param viewHolder        The ViewHolder which is being interacted by the User or it was
2026         *                          interacted and simply animating to its original position
2027         * @param dX                The amount of horizontal displacement caused by user's action
2028         * @param dY                The amount of vertical displacement caused by user's action
2029         * @param actionState       The type of interaction on the View. Is either {@link
2030         *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.
2031         * @param isCurrentlyActive True if this view is currently being controlled by the user or
2032         *                          false it is simply animating back to its original state.
2033         * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
2034         * boolean)
2035         */
2036        public void onChildDraw(Canvas c, RecyclerView recyclerView,
2037                ViewHolder viewHolder,
2038                float dX, float dY, int actionState, boolean isCurrentlyActive) {
2039            sUICallback.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState,
2040                    isCurrentlyActive);
2041        }
2042
2043        /**
2044         * Called by ItemTouchHelper on RecyclerView's onDraw callback.
2045         * <p>
2046         * If you would like to customize how your View's respond to user interactions, this is
2047         * a good place to override.
2048         * <p>
2049         * Default implementation translates the child by the given <code>dX</code>,
2050         * <code>dY</code>.
2051         * ItemTouchHelper also takes care of drawing the child after other children if it is being
2052         * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this
2053         * is
2054         * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L
2055         * and after, it changes View's elevation value to be greater than all other children.)
2056         *
2057         * @param c                 The canvas which RecyclerView is drawing its children
2058         * @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to
2059         * @param viewHolder        The ViewHolder which is being interacted by the User or it was
2060         *                          interacted and simply animating to its original position
2061         * @param dX                The amount of horizontal displacement caused by user's action
2062         * @param dY                The amount of vertical displacement caused by user's action
2063         * @param actionState       The type of interaction on the View. Is either {@link
2064         *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.
2065         * @param isCurrentlyActive True if this view is currently being controlled by the user or
2066         *                          false it is simply animating back to its original state.
2067         * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
2068         * boolean)
2069         */
2070        public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
2071                ViewHolder viewHolder,
2072                float dX, float dY, int actionState, boolean isCurrentlyActive) {
2073            sUICallback.onDrawOver(c, recyclerView, viewHolder.itemView, dX, dY, actionState,
2074                    isCurrentlyActive);
2075        }
2076
2077        /**
2078         * Called by the ItemTouchHelper when user action finished on a ViewHolder and now the View
2079         * will be animated to its final position.
2080         * <p>
2081         * Default implementation uses ItemAnimator's duration values. If
2082         * <code>animationType</code> is {@link #ANIMATION_TYPE_DRAG}, it returns
2083         * {@link RecyclerView.ItemAnimator#getMoveDuration()}, otherwise, it returns
2084         * {@link RecyclerView.ItemAnimator#getRemoveDuration()}. If RecyclerView does not have
2085         * any {@link RecyclerView.ItemAnimator} attached, this method returns
2086         * {@code DEFAULT_DRAG_ANIMATION_DURATION} or {@code DEFAULT_SWIPE_ANIMATION_DURATION}
2087         * depending on the animation type.
2088         *
2089         * @param recyclerView  The RecyclerView to which the ItemTouchHelper is attached to.
2090         * @param animationType The type of animation. Is one of {@link #ANIMATION_TYPE_DRAG},
2091         *                      {@link #ANIMATION_TYPE_SWIPE_CANCEL} or
2092         *                      {@link #ANIMATION_TYPE_SWIPE_SUCCESS}.
2093         * @param animateDx     The horizontal distance that the animation will offset
2094         * @param animateDy     The vertical distance that the animation will offset
2095         * @return The duration for the animation
2096         */
2097        public long getAnimationDuration(RecyclerView recyclerView, int animationType,
2098                float animateDx, float animateDy) {
2099            final RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();
2100            if (itemAnimator == null) {
2101                return animationType == ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION
2102                        : DEFAULT_SWIPE_ANIMATION_DURATION;
2103            } else {
2104                return animationType == ANIMATION_TYPE_DRAG ? itemAnimator.getMoveDuration()
2105                        : itemAnimator.getRemoveDuration();
2106            }
2107        }
2108
2109        /**
2110         * Called by the ItemTouchHelper when user is dragging a view out of bounds.
2111         * <p>
2112         * You can override this method to decide how much RecyclerView should scroll in response
2113         * to this action. Default implementation calculates a value based on the amount of View
2114         * out of bounds and the time it spent there. The longer user keeps the View out of bounds,
2115         * the faster the list will scroll. Similarly, the larger portion of the View is out of
2116         * bounds, the faster the RecyclerView will scroll.
2117         *
2118         * @param recyclerView        The RecyclerView instance to which ItemTouchHelper is
2119         *                            attached to.
2120         * @param viewSize            The total size of the View in scroll direction, excluding
2121         *                            item decorations.
2122         * @param viewSizeOutOfBounds The total size of the View that is out of bounds. This value
2123         *                            is negative if the View is dragged towards left or top edge.
2124         * @param totalSize           The total size of RecyclerView in the scroll direction.
2125         * @param msSinceStartScroll  The time passed since View is kept out of bounds.
2126         * @return The amount that RecyclerView should scroll. Keep in mind that this value will
2127         * be passed to {@link RecyclerView#scrollBy(int, int)} method.
2128         */
2129        public int interpolateOutOfBoundsScroll(RecyclerView recyclerView,
2130                int viewSize, int viewSizeOutOfBounds,
2131                int totalSize, long msSinceStartScroll) {
2132            final int maxScroll = getMaxDragScroll(recyclerView);
2133            final int absOutOfBounds = Math.abs(viewSizeOutOfBounds);
2134            final int direction = (int) Math.signum(viewSizeOutOfBounds);
2135            // might be negative if other direction
2136            float outOfBoundsRatio = Math.min(1f, 1f * absOutOfBounds / viewSize);
2137            final int cappedScroll = (int) (direction * maxScroll *
2138                    sDragViewScrollCapInterpolator.getInterpolation(outOfBoundsRatio));
2139            final float timeRatio;
2140            if (msSinceStartScroll > DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS) {
2141                timeRatio = 1f;
2142            } else {
2143                timeRatio = (float) msSinceStartScroll / DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS;
2144            }
2145            final int value = (int) (cappedScroll * sDragScrollInterpolator
2146                    .getInterpolation(timeRatio));
2147            if (value == 0) {
2148                return viewSizeOutOfBounds > 0 ? 1 : -1;
2149            }
2150            return value;
2151        }
2152    }
2153
2154    /**
2155     * A simple wrapper to the default Callback which you can construct with drag and swipe
2156     * directions and this class will handle the flag callbacks. You should still override onMove
2157     * or
2158     * onSwiped depending on your use case.
2159     *
2160     * <pre>
2161     * ItemTouchHelper mIth = new ItemTouchHelper(
2162     *     new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
2163     *         ItemTouchHelper.LEFT) {
2164     *         public abstract boolean onMove(RecyclerView recyclerView,
2165     *             ViewHolder viewHolder, ViewHolder target) {
2166     *             final int fromPos = viewHolder.getAdapterPosition();
2167     *             final int toPos = target.getAdapterPosition();
2168     *             // move item in `fromPos` to `toPos` in adapter.
2169     *             return true;// true if moved, false otherwise
2170     *         }
2171     *         public void onSwiped(ViewHolder viewHolder, int direction) {
2172     *             // remove from adapter
2173     *         }
2174     * });
2175     * </pre>
2176     */
2177    public abstract static class SimpleCallback extends Callback {
2178
2179        private int mDefaultSwipeDirs;
2180
2181        private int mDefaultDragDirs;
2182
2183        /**
2184         * Creates a Callback for the given drag and swipe allowance. These values serve as
2185         * defaults
2186         * and if you want to customize behavior per ViewHolder, you can override
2187         * {@link #getSwipeDirs(RecyclerView, ViewHolder)}
2188         * and / or {@link #getDragDirs(RecyclerView, ViewHolder)}.
2189         *
2190         * @param dragDirs  Binary OR of direction flags in which the Views can be dragged. Must be
2191         *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link
2192         *                  #END},
2193         *                  {@link #UP} and {@link #DOWN}.
2194         * @param swipeDirs Binary OR of direction flags in which the Views can be swiped. Must be
2195         *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link
2196         *                  #END},
2197         *                  {@link #UP} and {@link #DOWN}.
2198         */
2199        public SimpleCallback(int dragDirs, int swipeDirs) {
2200            mDefaultSwipeDirs = swipeDirs;
2201            mDefaultDragDirs = dragDirs;
2202        }
2203
2204        /**
2205         * Updates the default swipe directions. For example, you can use this method to toggle
2206         * certain directions depending on your use case.
2207         *
2208         * @param defaultSwipeDirs Binary OR of directions in which the ViewHolders can be swiped.
2209         */
2210        public void setDefaultSwipeDirs(int defaultSwipeDirs) {
2211            mDefaultSwipeDirs = defaultSwipeDirs;
2212        }
2213
2214        /**
2215         * Updates the default drag directions. For example, you can use this method to toggle
2216         * certain directions depending on your use case.
2217         *
2218         * @param defaultDragDirs Binary OR of directions in which the ViewHolders can be dragged.
2219         */
2220        public void setDefaultDragDirs(int defaultDragDirs) {
2221            mDefaultDragDirs = defaultDragDirs;
2222        }
2223
2224        /**
2225         * Returns the swipe directions for the provided ViewHolder.
2226         * Default implementation returns the swipe directions that was set via constructor or
2227         * {@link #setDefaultSwipeDirs(int)}.
2228         *
2229         * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.
2230         * @param viewHolder   The RecyclerView for which the swipe direction is queried.
2231         * @return A binary OR of direction flags.
2232         */
2233        public int getSwipeDirs(RecyclerView recyclerView, ViewHolder viewHolder) {
2234            return mDefaultSwipeDirs;
2235        }
2236
2237        /**
2238         * Returns the drag directions for the provided ViewHolder.
2239         * Default implementation returns the drag directions that was set via constructor or
2240         * {@link #setDefaultDragDirs(int)}.
2241         *
2242         * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.
2243         * @param viewHolder   The RecyclerView for which the swipe direction is queried.
2244         * @return A binary OR of direction flags.
2245         */
2246        public int getDragDirs(RecyclerView recyclerView, ViewHolder viewHolder) {
2247            return mDefaultDragDirs;
2248        }
2249
2250        @Override
2251        public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
2252            return makeMovementFlags(getDragDirs(recyclerView, viewHolder),
2253                    getSwipeDirs(recyclerView, viewHolder));
2254        }
2255    }
2256
2257    private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
2258
2259        ItemTouchHelperGestureListener() {
2260        }
2261
2262        @Override
2263        public boolean onDown(MotionEvent e) {
2264            return true;
2265        }
2266
2267        @Override
2268        public void onLongPress(MotionEvent e) {
2269            View child = findChildView(e);
2270            if (child != null) {
2271                ViewHolder vh = mRecyclerView.getChildViewHolder(child);
2272                if (vh != null) {
2273                    if (!mCallback.hasDragFlag(mRecyclerView, vh)) {
2274                        return;
2275                    }
2276                    int pointerId = e.getPointerId(0);
2277                    // Long press is deferred.
2278                    // Check w/ active pointer id to avoid selecting after motion
2279                    // event is canceled.
2280                    if (pointerId == mActivePointerId) {
2281                        final int index = e.findPointerIndex(mActivePointerId);
2282                        final float x = e.getX(index);
2283                        final float y = e.getY(index);
2284                        mInitialTouchX = x;
2285                        mInitialTouchY = y;
2286                        mDx = mDy = 0f;
2287                        if (DEBUG) {
2288                            Log.d(TAG,
2289                                    "onlong press: x:" + mInitialTouchX + ",y:" + mInitialTouchY);
2290                        }
2291                        if (mCallback.isLongPressDragEnabled()) {
2292                            select(vh, ACTION_STATE_DRAG);
2293                        }
2294                    }
2295                }
2296            }
2297        }
2298    }
2299
2300    private class RecoverAnimation implements AnimatorListenerCompat {
2301
2302        final float mStartDx;
2303
2304        final float mStartDy;
2305
2306        final float mTargetX;
2307
2308        final float mTargetY;
2309
2310        final ViewHolder mViewHolder;
2311
2312        final int mActionState;
2313
2314        private final ValueAnimatorCompat mValueAnimator;
2315
2316        final int mAnimationType;
2317
2318        public boolean mIsPendingCleanup;
2319
2320        float mX;
2321
2322        float mY;
2323
2324        // if user starts touching a recovering view, we put it into interaction mode again,
2325        // instantly.
2326        boolean mOverridden = false;
2327
2328        boolean mEnded = false;
2329
2330        private float mFraction;
2331
2332        public RecoverAnimation(ViewHolder viewHolder, int animationType,
2333                int actionState, float startDx, float startDy, float targetX, float targetY) {
2334            mActionState = actionState;
2335            mAnimationType = animationType;
2336            mViewHolder = viewHolder;
2337            mStartDx = startDx;
2338            mStartDy = startDy;
2339            mTargetX = targetX;
2340            mTargetY = targetY;
2341            mValueAnimator = AnimatorCompatHelper.emptyValueAnimator();
2342            mValueAnimator.addUpdateListener(
2343                    new AnimatorUpdateListenerCompat() {
2344                        @Override
2345                        public void onAnimationUpdate(ValueAnimatorCompat animation) {
2346                            setFraction(animation.getAnimatedFraction());
2347                        }
2348                    });
2349            mValueAnimator.setTarget(viewHolder.itemView);
2350            mValueAnimator.addListener(this);
2351            setFraction(0f);
2352        }
2353
2354        public void setDuration(long duration) {
2355            mValueAnimator.setDuration(duration);
2356        }
2357
2358        public void start() {
2359            mViewHolder.setIsRecyclable(false);
2360            mValueAnimator.start();
2361        }
2362
2363        public void cancel() {
2364            mValueAnimator.cancel();
2365        }
2366
2367        public void setFraction(float fraction) {
2368            mFraction = fraction;
2369        }
2370
2371        /**
2372         * We run updates on onDraw method but use the fraction from animator callback.
2373         * This way, we can sync translate x/y values w/ the animators to avoid one-off frames.
2374         */
2375        public void update() {
2376            if (mStartDx == mTargetX) {
2377                mX = ViewCompat.getTranslationX(mViewHolder.itemView);
2378            } else {
2379                mX = mStartDx + mFraction * (mTargetX - mStartDx);
2380            }
2381            if (mStartDy == mTargetY) {
2382                mY = ViewCompat.getTranslationY(mViewHolder.itemView);
2383            } else {
2384                mY = mStartDy + mFraction * (mTargetY - mStartDy);
2385            }
2386        }
2387
2388        @Override
2389        public void onAnimationStart(ValueAnimatorCompat animation) {
2390
2391        }
2392
2393        @Override
2394        public void onAnimationEnd(ValueAnimatorCompat animation) {
2395            if (!mEnded) {
2396                mViewHolder.setIsRecyclable(true);
2397            }
2398            mEnded = true;
2399        }
2400
2401        @Override
2402        public void onAnimationCancel(ValueAnimatorCompat animation) {
2403            setFraction(1f); //make sure we recover the view's state.
2404        }
2405
2406        @Override
2407        public void onAnimationRepeat(ValueAnimatorCompat animation) {
2408
2409        }
2410    }
2411}