• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.music;
18 
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.content.res.Resources;
22 import android.graphics.Bitmap;
23 import android.graphics.PixelFormat;
24 import android.graphics.Rect;
25 import android.util.AttributeSet;
26 import android.view.GestureDetector;
27 import android.view.Gravity;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.view.ViewConfiguration;
31 import android.view.ViewGroup;
32 import android.view.WindowManager;
33 import android.view.GestureDetector.SimpleOnGestureListener;
34 import android.widget.AdapterView;
35 import android.widget.ImageView;
36 import android.widget.ListView;
37 
38 public class TouchInterceptor extends ListView {
39 
40     private ImageView mDragView;
41     private WindowManager mWindowManager;
42     private WindowManager.LayoutParams mWindowParams;
43     private int mDragPos;      // which item is being dragged
44     private int mFirstDragPos; // where was the dragged item originally
45     private int mDragPoint;    // at what offset inside the item did the user grab it
46     private int mCoordOffset;  // the difference between screen coordinates and coordinates in this view
47     private DragListener mDragListener;
48     private DropListener mDropListener;
49     private RemoveListener mRemoveListener;
50     private int mUpperBound;
51     private int mLowerBound;
52     private int mHeight;
53     private GestureDetector mGestureDetector;
54     private static final int FLING = 0;
55     private static final int SLIDE = 1;
56     private int mRemoveMode = -1;
57     private Rect mTempRect = new Rect();
58     private Bitmap mDragBitmap;
59     private final int mTouchSlop;
60     private int mItemHeightNormal;
61     private int mItemHeightExpanded;
62 
TouchInterceptor(Context context, AttributeSet attrs)63     public TouchInterceptor(Context context, AttributeSet attrs) {
64         super(context, attrs);
65         SharedPreferences pref = context.getSharedPreferences("Music", 3);
66         mRemoveMode = pref.getInt("deletemode", -1);
67         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
68         Resources res = getResources();
69         mItemHeightNormal = res.getDimensionPixelSize(R.dimen.normal_height);
70         mItemHeightExpanded = res.getDimensionPixelSize(R.dimen.expanded_height);
71     }
72 
73     @Override
onInterceptTouchEvent(MotionEvent ev)74     public boolean onInterceptTouchEvent(MotionEvent ev) {
75         if (mRemoveListener != null && mGestureDetector == null) {
76             if (mRemoveMode == FLING) {
77                 mGestureDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {
78                     @Override
79                     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
80                             float velocityY) {
81                         if (mDragView != null) {
82                             if (velocityX > 1000) {
83                                 Rect r = mTempRect;
84                                 mDragView.getDrawingRect(r);
85                                 if ( e2.getX() > r.right * 2 / 3) {
86                                     // fast fling right with release near the right edge of the screen
87                                     stopDragging();
88                                     mRemoveListener.remove(mFirstDragPos);
89                                     unExpandViews(true);
90                                 }
91                             }
92                             // flinging while dragging should have no effect
93                             return true;
94                         }
95                         return false;
96                     }
97                 });
98             }
99         }
100         if (mDragListener != null || mDropListener != null) {
101             switch (ev.getAction()) {
102                 case MotionEvent.ACTION_DOWN:
103                     int x = (int) ev.getX();
104                     int y = (int) ev.getY();
105                     int itemnum = pointToPosition(x, y);
106                     if (itemnum == AdapterView.INVALID_POSITION) {
107                         break;
108                     }
109                     ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
110                     mDragPoint = y - item.getTop();
111                     mCoordOffset = ((int)ev.getRawY()) - y;
112                     View dragger = item.findViewById(R.id.icon);
113                     Rect r = mTempRect;
114                     dragger.getDrawingRect(r);
115                     // The dragger icon itself is quite small, so pretend the touch area is bigger
116                     if (x < r.right * 2) {
117                         item.setDrawingCacheEnabled(true);
118                         // Create a copy of the drawing cache so that it does not get recycled
119                         // by the framework when the list tries to clean up memory
120                         Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
121                         startDragging(bitmap, y);
122                         mDragPos = itemnum;
123                         mFirstDragPos = mDragPos;
124                         mHeight = getHeight();
125                         int touchSlop = mTouchSlop;
126                         mUpperBound = Math.min(y - touchSlop, mHeight / 3);
127                         mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
128                         return false;
129                     }
130                     mDragView = null;
131                     break;
132             }
133         }
134         return super.onInterceptTouchEvent(ev);
135     }
136 
137     /*
138      * pointToPosition() doesn't consider invisible views, but we
139      * need to, so implement a slightly different version.
140      */
myPointToPosition(int x, int y)141     private int myPointToPosition(int x, int y) {
142         Rect frame = mTempRect;
143         final int count = getChildCount();
144         for (int i = count - 1; i >= 0; i--) {
145             final View child = getChildAt(i);
146             child.getHitRect(frame);
147             if (frame.contains(x, y)) {
148                 return getFirstVisiblePosition() + i;
149             }
150         }
151         return INVALID_POSITION;
152     }
153 
getItemForPosition(int y)154     private int getItemForPosition(int y) {
155         int adjustedy = y - mDragPoint - 32;
156         int pos = myPointToPosition(0, adjustedy);
157         if (pos >= 0) {
158             if (pos <= mFirstDragPos) {
159                 pos += 1;
160             }
161         } else if (adjustedy < 0) {
162             pos = 0;
163         }
164         return pos;
165     }
166 
adjustScrollBounds(int y)167     private void adjustScrollBounds(int y) {
168         if (y >= mHeight / 3) {
169             mUpperBound = mHeight / 3;
170         }
171         if (y <= mHeight * 2 / 3) {
172             mLowerBound = mHeight * 2 / 3;
173         }
174     }
175 
176     /*
177      * Restore size and visibility for all listitems
178      */
unExpandViews(boolean deletion)179     private void unExpandViews(boolean deletion) {
180         for (int i = 0;; i++) {
181             View v = getChildAt(i);
182             if (v == null) {
183                 if (deletion) {
184                     // HACK force update of mItemCount
185                     int position = getFirstVisiblePosition();
186                     int y = getChildAt(0).getTop();
187                     setAdapter(getAdapter());
188                     setSelectionFromTop(position, y);
189                     // end hack
190                 }
191                 layoutChildren(); // force children to be recreated where needed
192                 v = getChildAt(i);
193                 if (v == null) {
194                     break;
195                 }
196             }
197             ViewGroup.LayoutParams params = v.getLayoutParams();
198             params.height = mItemHeightNormal;
199             v.setLayoutParams(params);
200             v.setVisibility(View.VISIBLE);
201         }
202     }
203 
204     /* Adjust visibility and size to make it appear as though
205      * an item is being dragged around and other items are making
206      * room for it:
207      * If dropping the item would result in it still being in the
208      * same place, then make the dragged listitem's size normal,
209      * but make the item invisible.
210      * Otherwise, if the dragged listitem is still on screen, make
211      * it as small as possible and expand the item below the insert
212      * point.
213      * If the dragged item is not on screen, only expand the item
214      * below the current insertpoint.
215      */
doExpansion()216     private void doExpansion() {
217         int childnum = mDragPos - getFirstVisiblePosition();
218         if (mDragPos > mFirstDragPos) {
219             childnum++;
220         }
221 
222         View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());
223 
224         for (int i = 0;; i++) {
225             View vv = getChildAt(i);
226             if (vv == null) {
227                 break;
228             }
229             int height = mItemHeightNormal;
230             int visibility = View.VISIBLE;
231             if (vv.equals(first)) {
232                 // processing the item that is being dragged
233                 if (mDragPos == mFirstDragPos) {
234                     // hovering over the original location
235                     visibility = View.INVISIBLE;
236                 } else {
237                     // not hovering over it
238                     height = 1;
239                 }
240             } else if (i == childnum) {
241                 if (mDragPos < getCount() - 1) {
242                     height = mItemHeightExpanded;
243                 }
244             }
245             ViewGroup.LayoutParams params = vv.getLayoutParams();
246             params.height = height;
247             vv.setLayoutParams(params);
248             vv.setVisibility(visibility);
249         }
250     }
251 
252     @Override
onTouchEvent(MotionEvent ev)253     public boolean onTouchEvent(MotionEvent ev) {
254         if (mGestureDetector != null) {
255             mGestureDetector.onTouchEvent(ev);
256         }
257         if ((mDragListener != null || mDropListener != null) && mDragView != null) {
258             int action = ev.getAction();
259             switch (action) {
260                 case MotionEvent.ACTION_UP:
261                 case MotionEvent.ACTION_CANCEL:
262                     Rect r = mTempRect;
263                     mDragView.getDrawingRect(r);
264                     stopDragging();
265                     if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) {
266                         if (mRemoveListener != null) {
267                             mRemoveListener.remove(mFirstDragPos);
268                         }
269                         unExpandViews(true);
270                     } else {
271                         if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
272                             mDropListener.drop(mFirstDragPos, mDragPos);
273                         }
274                         unExpandViews(false);
275                     }
276                     break;
277 
278                 case MotionEvent.ACTION_DOWN:
279                 case MotionEvent.ACTION_MOVE:
280                     int x = (int) ev.getX();
281                     int y = (int) ev.getY();
282                     dragView(x, y);
283                     int itemnum = getItemForPosition(y);
284                     if (itemnum >= 0) {
285                         if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
286                             if (mDragListener != null) {
287                                 mDragListener.drag(mDragPos, itemnum);
288                             }
289                             mDragPos = itemnum;
290                             doExpansion();
291                         }
292                         int speed = 0;
293                         adjustScrollBounds(y);
294                         if (y > mLowerBound) {
295                             // scroll the list up a bit
296                             speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
297                         } else if (y < mUpperBound) {
298                             // scroll the list down a bit
299                             speed = y < mUpperBound / 2 ? -16 : -4;
300                         }
301                         if (speed != 0) {
302                             int ref = pointToPosition(0, mHeight / 2);
303                             if (ref == AdapterView.INVALID_POSITION) {
304                                 //we hit a divider or an invisible view, check somewhere else
305                                 ref = pointToPosition(0, mHeight / 2 + getDividerHeight() + 64);
306                             }
307                             View v = getChildAt(ref - getFirstVisiblePosition());
308                             if (v!= null) {
309                                 int pos = v.getTop();
310                                 setSelectionFromTop(ref, pos - speed);
311                             }
312                         }
313                     }
314                     break;
315             }
316             return true;
317         }
318         return super.onTouchEvent(ev);
319     }
320 
321     private void startDragging(Bitmap bm, int y) {
322         stopDragging();
323 
324         mWindowParams = new WindowManager.LayoutParams();
325         mWindowParams.gravity = Gravity.TOP;
326         mWindowParams.x = 0;
327         mWindowParams.y = y - mDragPoint + mCoordOffset;
328 
329         mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
330         mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
331         mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
332                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
333                 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
334                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
335         mWindowParams.format = PixelFormat.TRANSLUCENT;
336         mWindowParams.windowAnimations = 0;
337 
338         ImageView v = new ImageView(mContext);
339         int backGroundColor = mContext.getResources().getColor(R.color.dragndrop_background);
340         v.setBackgroundColor(backGroundColor);
341         v.setImageBitmap(bm);
342         mDragBitmap = bm;
343 
344         mWindowManager = (WindowManager)mContext.getSystemService("window");
345         mWindowManager.addView(v, mWindowParams);
346         mDragView = v;
347     }
348 
349     private void dragView(int x, int y) {
350         if (mRemoveMode == SLIDE) {
351             float alpha = 1.0f;
352             int width = mDragView.getWidth();
353             if (x > width / 2) {
354                 alpha = ((float)(width - x)) / (width / 2);
355             }
356             mWindowParams.alpha = alpha;
357         }
358         mWindowParams.y = y - mDragPoint + mCoordOffset;
359         mWindowManager.updateViewLayout(mDragView, mWindowParams);
360     }
361 
stopDragging()362     private void stopDragging() {
363         if (mDragView != null) {
364             WindowManager wm = (WindowManager)mContext.getSystemService("window");
365             wm.removeView(mDragView);
366             mDragView.setImageDrawable(null);
367             mDragView = null;
368         }
369         if (mDragBitmap != null) {
370             mDragBitmap.recycle();
371             mDragBitmap = null;
372         }
373     }
374 
setDragListener(DragListener l)375     public void setDragListener(DragListener l) {
376         mDragListener = l;
377     }
378 
setDropListener(DropListener l)379     public void setDropListener(DropListener l) {
380         mDropListener = l;
381     }
382 
setRemoveListener(RemoveListener l)383     public void setRemoveListener(RemoveListener l) {
384         mRemoveListener = l;
385     }
386 
387     public interface DragListener {
388         void drag(int from, int to);
389     }
390     public interface DropListener {
391         void drop(int from, int to);
392     }
393     public interface RemoveListener {
394         void remove(int which);
395     }
396 }
397