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