OSDN Git Service

1aa5e8a483a1385c7706498a90d58b2b8fcf13fd
[android-x86/packages-apps-Eleven.git] / src / com / andrew / apollo / dragdrop / DragSortController.java
1
2 package com.andrew.apollo.dragdrop;
3
4 import android.graphics.Point;
5 import android.view.GestureDetector;
6 import android.view.HapticFeedbackConstants;
7 import android.view.MotionEvent;
8 import android.view.View;
9 import android.view.ViewConfiguration;
10 import android.widget.AdapterView;
11
12 /**
13  * Class that starts and stops item drags on a {@link DragSortListView} based on
14  * touch gestures. This class also inherits from {@link SimpleFloatViewManager},
15  * which provides basic float View creation. An instance of this class is meant
16  * to be passed to the methods {@link DragSortListView#setTouchListener()} and
17  * {@link DragSortListView#setFloatViewManager()} of your
18  * {@link DragSortListView} instance.
19  */
20 public class DragSortController extends SimpleFloatViewManager implements View.OnTouchListener,
21         GestureDetector.OnGestureListener {
22
23     public final static int ON_DOWN = 0;
24
25     public final static int ON_DRAG = 1;
26
27     public final static int ON_LONG_PRESS = 2;
28
29     public final static int FLING_RIGHT_REMOVE = 0;
30
31     public final static int FLING_LEFT_REMOVE = 1;
32
33     public final static int SLIDE_RIGHT_REMOVE = 2;
34
35     public final static int SLIDE_LEFT_REMOVE = 3;
36
37     public final static int MISS = -1;
38
39     private final GestureDetector mDetector;
40
41     private final GestureDetector mFlingRemoveDetector;
42
43     private final int mTouchSlop;
44
45     private final int[] mTempLoc = new int[2];
46
47     private final float mFlingSpeed = 500f;
48
49     private final DragSortListView mDslv;
50
51     private boolean mSortEnabled = true;
52
53     private boolean mRemoveEnabled = false;
54
55     private boolean mDragging = false;
56
57     private int mDragInitMode = ON_DOWN;
58
59     private int mRemoveMode;
60
61     private int mHitPos = MISS;
62
63     private int mItemX;
64
65     private int mItemY;
66
67     private int mCurrX;
68
69     private int mCurrY;
70
71     private int mDragHandleId;
72
73     private float mOrigFloatAlpha = 1.0f;
74
75     /**
76      * Calls {@link #DragSortController(DragSortListView, int)} with a 0 drag
77      * handle id, FLING_RIGHT_REMOVE remove mode, and ON_DOWN drag init. By
78      * default, sorting is enabled, and removal is disabled.
79      * 
80      * @param dslv The DSLV instance
81      */
82     public DragSortController(DragSortListView dslv) {
83         this(dslv, 0, ON_DOWN, FLING_RIGHT_REMOVE);
84     }
85
86     /**
87      * By default, sorting is enabled, and removal is disabled.
88      * 
89      * @param dslv The DSLV instance
90      * @param dragHandleId The resource id of the View that represents the drag
91      *            handle in a list item.
92      */
93     public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode,
94             int removeMode) {
95         super(dslv);
96         mDslv = dslv;
97         mDetector = new GestureDetector(dslv.getContext(), this);
98         mFlingRemoveDetector = new GestureDetector(dslv.getContext(), mFlingRemoveListener);
99         mFlingRemoveDetector.setIsLongpressEnabled(false);
100         mTouchSlop = ViewConfiguration.get(dslv.getContext()).getScaledTouchSlop();
101         mDragHandleId = dragHandleId;
102         setRemoveMode(removeMode);
103         setDragInitMode(dragInitMode);
104         mOrigFloatAlpha = dslv.getFloatAlpha();
105     }
106
107     /**
108      * @return The current drag init mode.
109      */
110     public int getDragInitMode() {
111         return mDragInitMode;
112     }
113
114     /**
115      * Set how a drag is initiated. Needs to be one of {@link ON_DOWN},
116      * {@link ON_DRAG}, or {@link ON_LONG_PRESS}.
117      * 
118      * @param mode The drag init mode.
119      */
120     public void setDragInitMode(int mode) {
121         mDragInitMode = mode;
122     }
123
124     /**
125      * Enable/Disable list item sorting. Disabling is useful if only item
126      * removal is desired. Prevents drags in the vertical direction.
127      * 
128      * @param enabled Set <code>true</code> to enable list item sorting.
129      */
130     public void setSortEnabled(boolean enabled) {
131         mSortEnabled = enabled;
132     }
133
134     /**
135      * @return True if sort is enabled, false otherwise.
136      */
137     public boolean isSortEnabled() {
138         return mSortEnabled;
139     }
140
141     /**
142      * One of {@link FLING_RIGHT_REMOVE}, {@link FLING_LEFT_REMOVE},
143      * {@link SLIDE_RIGHT_REMOVE}, or {@link SLIDE_LEFT_REMOVE}.
144      */
145     public void setRemoveMode(int mode) {
146         mRemoveMode = mode;
147     }
148
149     /**
150      * @return The current remove mode.
151      */
152     public int getRemoveMode() {
153         return mRemoveMode;
154     }
155
156     /**
157      * Enable/Disable item removal without affecting remove mode.
158      */
159     public void setRemoveEnabled(boolean enabled) {
160         mRemoveEnabled = enabled;
161     }
162
163     /**
164      * @return True if remove is enabled, false otherwise.
165      */
166     public boolean isRemoveEnabled() {
167         return mRemoveEnabled;
168     }
169
170     /**
171      * Set the resource id for the View that represents the drag handle in a
172      * list item.
173      * 
174      * @param id An android resource id.
175      */
176     public void setDragHandleId(int id) {
177         mDragHandleId = id;
178     }
179
180     /**
181      * Sets flags to restrict certain motions of the floating View based on
182      * DragSortController settings (such as remove mode). Starts the drag on the
183      * DragSortListView.
184      * 
185      * @param position The list item position (includes headers).
186      * @param deltaX Touch x-coord minus left edge of floating View.
187      * @param deltaY Touch y-coord minus top edge of floating View.
188      * @return True if drag started, false otherwise.
189      */
190     public boolean startDrag(int position, int deltaX, int deltaY) {
191
192         int mDragFlags = 0;
193         if (mSortEnabled) {
194             mDragFlags |= DragSortListView.DRAG_POS_Y | DragSortListView.DRAG_NEG_Y;
195         }
196
197         if (mRemoveEnabled) {
198             if (mRemoveMode == FLING_RIGHT_REMOVE) {
199                 mDragFlags |= DragSortListView.DRAG_POS_X;
200             } else if (mRemoveMode == FLING_LEFT_REMOVE) {
201                 mDragFlags |= DragSortListView.DRAG_NEG_X;
202             }
203         }
204
205         mDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), mDragFlags, deltaX,
206                 deltaY);
207         return mDragging;
208     }
209
210     /**
211      * {@inheritDoc}
212      */
213     @Override
214     public boolean onTouch(View v, MotionEvent ev) {
215         mDetector.onTouchEvent(ev);
216         if (mRemoveEnabled && mDragging
217                 && (mRemoveMode == FLING_RIGHT_REMOVE || mRemoveMode == FLING_LEFT_REMOVE)) {
218             mFlingRemoveDetector.onTouchEvent(ev);
219         }
220
221         final int mAction = ev.getAction() & MotionEvent.ACTION_MASK;
222
223         switch (mAction) {
224             case MotionEvent.ACTION_DOWN:
225                 mCurrX = (int)ev.getX();
226                 mCurrY = (int)ev.getY();
227                 break;
228             case MotionEvent.ACTION_UP:
229                 if (mRemoveEnabled) {
230                     final int x = (int)ev.getX();
231                     int thirdW = mDslv.getWidth() / 3;
232                     int twoThirdW = mDslv.getWidth() - thirdW;
233                     if ((mRemoveMode == SLIDE_RIGHT_REMOVE && x > twoThirdW)
234                             || (mRemoveMode == SLIDE_LEFT_REMOVE && x < thirdW)) {
235                         mDslv.stopDrag(true);
236                     }
237                 }
238             case MotionEvent.ACTION_CANCEL:
239                 mDragging = false;
240                 break;
241         }
242         return false;
243     }
244
245     /**
246      * Overrides to provide fading when slide removal is enabled.
247      */
248     @Override
249     public void onDragFloatView(View floatView, Point position, Point touch) {
250
251         if (mRemoveEnabled) {
252             int x = touch.x;
253
254             if (mRemoveMode == SLIDE_RIGHT_REMOVE) {
255                 int width = mDslv.getWidth();
256                 int thirdWidth = width / 3;
257
258                 float alpha;
259                 if (x < thirdWidth) {
260                     alpha = 1.0f;
261                 } else if (x < width - thirdWidth) {
262                     alpha = ((float)(width - thirdWidth - x)) / ((float)thirdWidth);
263                 } else {
264                     alpha = 0.0f;
265                 }
266                 mDslv.setFloatAlpha(mOrigFloatAlpha * alpha);
267             } else if (mRemoveMode == SLIDE_LEFT_REMOVE) {
268                 int width = mDslv.getWidth();
269                 int thirdWidth = width / 3;
270
271                 float alpha;
272                 if (x < thirdWidth) {
273                     alpha = 0.0f;
274                 } else if (x < width - thirdWidth) {
275                     alpha = ((float)(x - thirdWidth)) / ((float)thirdWidth);
276                 } else {
277                     alpha = 1.0f;
278                 }
279                 mDslv.setFloatAlpha(mOrigFloatAlpha * alpha);
280             }
281         }
282     }
283
284     /**
285      * Get the position to start dragging based on the ACTION_DOWN MotionEvent.
286      * This function simply calls {@link #dragHandleHitPosition(MotionEvent)}.
287      * Override to change drag handle behavior; this function is called
288      * internally when an ACTION_DOWN event is detected.
289      * 
290      * @param ev The ACTION_DOWN MotionEvent.
291      * @return The list position to drag if a drag-init gesture is detected;
292      *         MISS if unsuccessful.
293      */
294     public int startDragPosition(MotionEvent ev) {
295         return dragHandleHitPosition(ev);
296     }
297
298     /**
299      * Checks for the touch of an item's drag handle (specified by
300      * {@link #setDragHandleId(int)}), and returns that item's position if a
301      * drag handle touch was detected.
302      * 
303      * @param ev The ACTION_DOWN MotionEvent.
304      * @return The list position of the item whose drag handle was touched; MISS
305      *         if unsuccessful.
306      */
307     public int dragHandleHitPosition(MotionEvent ev) {
308         final int x = (int)ev.getX();
309         final int y = (int)ev.getY();
310
311         int touchPos = mDslv.pointToPosition(x, y);
312
313         final int numHeaders = mDslv.getHeaderViewsCount();
314         final int numFooters = mDslv.getFooterViewsCount();
315         final int count = mDslv.getCount();
316
317         if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders
318                 && touchPos < (count - numFooters)) {
319             final View item = mDslv.getChildAt(touchPos - mDslv.getFirstVisiblePosition());
320             final int rawX = (int)ev.getRawX();
321             final int rawY = (int)ev.getRawY();
322
323             View dragBox = item.findViewById(mDragHandleId);
324             if (dragBox != null) {
325                 dragBox.getLocationOnScreen(mTempLoc);
326
327                 if (rawX > mTempLoc[0] && rawY > mTempLoc[1]
328                         && rawX < mTempLoc[0] + dragBox.getWidth()
329                         && rawY < mTempLoc[1] + dragBox.getHeight()) {
330
331                     mItemX = item.getLeft();
332                     mItemY = item.getTop();
333
334                     return touchPos;
335                 }
336             }
337         }
338         return MISS;
339     }
340
341     /**
342      * {@inheritDoc}
343      */
344     @Override
345     public boolean onDown(MotionEvent ev) {
346         mHitPos = startDragPosition(ev);
347
348         if (mHitPos != MISS && mDragInitMode == ON_DOWN) {
349             startDrag(mHitPos, (int)ev.getX() - mItemX, (int)ev.getY() - mItemY);
350         }
351
352         return true;
353     }
354
355     /**
356      * {@inheritDoc}
357      */
358     @Override
359     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
360         if (mHitPos != MISS && mDragInitMode == ON_DRAG && !mDragging) {
361             final int x1 = (int)e1.getX();
362             final int y1 = (int)e1.getY();
363             final int x2 = (int)e2.getX();
364             final int y2 = (int)e2.getY();
365
366             boolean start = false;
367             if (mRemoveEnabled && mSortEnabled) {
368                 start = true;
369             } else if (mRemoveEnabled) {
370                 start = Math.abs(x2 - x1) > mTouchSlop;
371             } else if (mSortEnabled) {
372                 start = Math.abs(y2 - y1) > mTouchSlop;
373             }
374
375             if (start) {
376                 startDrag(mHitPos, x2 - mItemX, y2 - mItemY);
377             }
378         }
379         return false;
380     }
381
382     /**
383      * {@inheritDoc}
384      */
385     @Override
386     public void onLongPress(MotionEvent e) {
387         if (mHitPos != MISS && mDragInitMode == ON_LONG_PRESS) {
388             mDslv.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
389             startDrag(mHitPos, mCurrX - mItemX, mCurrY - mItemY);
390         }
391     }
392
393     /**
394      * {@inheritDoc}
395      */
396     @Override
397     public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
398         return false;
399     }
400
401     /**
402      * {@inheritDoc}
403      */
404     @Override
405     public boolean onSingleTapUp(MotionEvent ev) {
406         return false;
407     }
408
409     /**
410      * {@inheritDoc}
411      */
412     @Override
413     public void onShowPress(MotionEvent ev) {
414     }
415
416     private final GestureDetector.OnGestureListener mFlingRemoveListener = new GestureDetector.SimpleOnGestureListener() {
417
418         /**
419          * {@inheritDoc}
420          */
421         @Override
422         public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
423                 float velocityY) {
424             if (mRemoveEnabled) {
425                 switch (mRemoveMode) {
426                     case FLING_RIGHT_REMOVE:
427                         if (velocityX > mFlingSpeed) {
428                             mDslv.stopDrag(true);
429                         }
430                         break;
431                     case FLING_LEFT_REMOVE:
432                         if (velocityX < -mFlingSpeed) {
433                             mDslv.stopDrag(true);
434                         }
435                         break;
436                 }
437             }
438             return false;
439         }
440     };
441
442 }