Added Android code
[wl-app.git] / Android / webViewMarker / src / main / java / com / blahti / drag / DragController.java
1 /*
2  * This is a modified version of a class from the Android
3  * Open Source Project. The original copyright and license information follows.
4  * 
5  * Copyright (C) 2008 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 package com.blahti.drag;
21
22 import android.content.Context;
23 import android.graphics.Bitmap;
24 import android.graphics.Rect;
25 import android.os.IBinder;
26 import android.util.DisplayMetrics;
27 import android.util.Log;
28 import android.view.View;
29 import android.view.KeyEvent;
30 import android.view.MotionEvent;
31 import android.view.WindowManager;
32 import android.view.inputmethod.InputMethodManager;
33
34 import java.util.ArrayList;
35
36 /**
37  * This class is used to initiate a drag within a view or across multiple views.
38  * When a drag starts it creates a special view (a DragView) that moves around the screen
39  * until the user ends the drag. As feedback to the user, this object causes the device to
40  * vibrate as the drag begins.
41  *
42  */
43
44 public class DragController {
45     public enum DragBehavior {
46         MOVE,   // indicates the drag is move
47         COPY    // indicates the drag is copy
48     }
49     public static final String TAG = "DragController";
50
51     private Context mContext;
52     private Rect mRectTemp = new Rect();
53     private final int[] mCoordinatesTemp = new int[2];
54     private boolean mDragging;
55     private float mMotionDownX;
56     private float mMotionDownY;
57     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
58
59     /** Original view that is being dragged.  */
60     private View mOriginator;
61
62     /** X offset from the upper-left corner of the cell to where we touched.  */
63     private float mTouchOffsetX;
64
65     /** Y offset from the upper-left corner of the cell to where we touched.  */
66     private float mTouchOffsetY;
67
68     /** Where the drag originated */
69     private DragSource mDragSource;
70
71     /** The data associated with the object being dragged */
72     private Object mDragInfo;
73
74     /** The view that moves around while you drag.  */
75     private DragView mDragView;
76
77     /** Who can receive drop events */
78     private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
79
80     private DragListener mListener;
81
82     /** The window token used as the parent for the DragView. */
83     private IBinder mWindowToken;
84
85     private View mMoveTarget;
86
87     private DropTarget mLastDropTarget;
88
89     private InputMethodManager mInputMethodManager;
90
91
92     /**
93      * Used to create a new DragLayer from XML.
94      *
95      * @param context The application's context.
96      */
97     public DragController(Context context) {
98         mContext = context;
99     }
100
101     /**
102      * Starts a drag. 
103      * It creates a bitmap of the view being dragged. That bitmap is what you see moving.
104      * The actual view can be repositioned if that is what the onDrop handle chooses to do.
105      * 
106      * @param v The view that is being dragged
107      * @param source An object representing where the drag originated
108      * @param dragInfo The data associated with the object that is being dragged
109      * @param dragAction The drag behavior: move or copy
110      */
111     public void startDrag(View v, DragSource source, Object dragInfo, DragBehavior dragBehavior) {
112         if (source.allowDrag()) {
113             mOriginator = v;
114             final Bitmap b = getViewBitmap(v);
115             if (b != null) {
116                 final int[] loc = mCoordinatesTemp;
117                 v.getLocationOnScreen(loc);
118                 final int screenX = loc[0];
119                 final int screenY = loc[1];
120                 startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(), source, dragInfo, dragBehavior);
121                 b.recycle();
122                 if (dragBehavior == DragBehavior.MOVE) {
123                     v.setVisibility(View.GONE);
124                 }
125             }
126         }
127     }
128
129     /**
130      * Starts a drag.
131      * 
132      * @param b The bitmap to display as the drag image.  It will be re-scaled to the
133      *          enlarged size.
134      * @param screenX The x position on screen of the left-top of the bitmap.
135      * @param screenY The y position on screen of the left-top of the bitmap.
136      * @param textureLeft The left edge of the region inside b to use.
137      * @param textureTop The top edge of the region inside b to use.
138      * @param textureWidth The width of the region inside b to use.
139      * @param textureHeight The height of the region inside b to use.
140      * @param source An object representing where the drag originated
141      * @param dragInfo The data associated with the object that is being dragged
142      * @param dragBehavior The drag action: move or copy
143      */
144     private void startDrag(Bitmap b, int screenX, int screenY, int textureLeft, int textureTop, int textureWidth, int textureHeight, DragSource source, Object dragInfo, DragBehavior dragBehavior) {
145         if (mInputMethodManager == null) {
146             mInputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
147         }
148         mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
149         if (mListener != null) {
150             mListener.onDragStart(source, dragInfo, dragBehavior);
151         }
152         final int registrationX = ((int)mMotionDownX) - screenX;
153         final int registrationY = ((int)mMotionDownY) - screenY;
154         mTouchOffsetX = mMotionDownX - screenX;
155         mTouchOffsetY = mMotionDownY - screenY;
156         mDragging = true;
157         mDragSource = source;
158         mDragInfo = dragInfo;
159         mDragView = new DragView(mContext, b, registrationX, registrationY, textureLeft, textureTop, textureWidth, textureHeight);
160         mDragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
161     }
162
163     /**
164      * Draw the view into a bitmap.
165      */
166     private Bitmap getViewBitmap(View v) {
167         v.clearFocus();
168         v.setPressed(false);
169
170         boolean willNotCache = v.willNotCacheDrawing();
171         v.setWillNotCacheDrawing(false);
172
173         // Reset the drawing cache background color to fully transparent
174         // for the duration of this operation
175         int color = v.getDrawingCacheBackgroundColor();
176         v.setDrawingCacheBackgroundColor(0);
177
178         if (color != 0) {
179             v.destroyDrawingCache();
180         }
181         v.buildDrawingCache();
182         Bitmap cacheBitmap = v.getDrawingCache();
183         if (cacheBitmap == null) {
184             Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
185             return null;
186         }
187
188         Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
189
190         // Restore the view
191         v.destroyDrawingCache();
192         v.setWillNotCacheDrawing(willNotCache);
193         v.setDrawingCacheBackgroundColor(color);
194
195         return bitmap;
196     }
197
198     public boolean dispatchKeyEvent(KeyEvent event) {
199         return mDragging;
200     }
201
202     public void cancelDrag() {
203         endDrag();
204     }
205
206     private void endDrag() {
207         if (mDragging) {
208             mDragging = false;
209             if (mOriginator != null) {
210                 mOriginator.setVisibility(View.VISIBLE);
211             }
212             if (mListener != null) {
213                 mListener.onDragEnd();
214             }
215             if (mDragView != null) {
216                 mDragView.remove();
217                 mDragView = null;
218             }
219         }
220     }
221
222     public boolean onInterceptTouchEvent(MotionEvent ev) {
223         final int action = ev.getAction();
224         if (action == MotionEvent.ACTION_DOWN) {
225             recordScreenSize();
226         }
227         final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels);
228         final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels);
229         switch (action) {
230             case MotionEvent.ACTION_MOVE:
231                 break;
232             case MotionEvent.ACTION_DOWN:
233                 mMotionDownX = screenX;
234                 mMotionDownY = screenY;
235                 mLastDropTarget = null;
236                 break;
237             case MotionEvent.ACTION_CANCEL:
238             case MotionEvent.ACTION_UP:
239                 if (mDragging) {
240                     drop(screenX, screenY);
241                 }
242                 endDrag();
243                 break;
244         }
245         return mDragging;
246     }
247
248     /**
249      * Sets the view that should handle move events.
250      */
251     void setMoveTarget(View view) {
252         mMoveTarget = view;
253     }
254
255     public boolean dispatchUnhandledMove(View focused, int direction) {
256         return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
257     }
258
259     public boolean onTouchEvent(MotionEvent ev) {
260         if (!mDragging) {
261             return false;
262         }
263
264         final int action = ev.getAction();
265         final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels);
266         final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels);
267
268         switch (action) {
269         case MotionEvent.ACTION_DOWN:
270             mMotionDownX = screenX;
271             mMotionDownY = screenY;
272             break;
273         case MotionEvent.ACTION_MOVE:
274             mDragView.move((int)ev.getRawX(), (int)ev.getRawY());
275             final int[] coordinates = mCoordinatesTemp;
276             DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates);
277             if (dropTarget != null) {
278                 if (mLastDropTarget == dropTarget) {
279                     dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1], (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo);
280                 }
281                 else {
282                     if (mLastDropTarget != null) {
283                         mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
284                     }
285                     dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1], (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo);
286                 }
287             }
288             else {
289                 if (mLastDropTarget != null) {
290                     mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo);
291                 }
292             }
293             mLastDropTarget = dropTarget;
294             break;
295         case MotionEvent.ACTION_UP:
296             if (mDragging) {
297                 drop(screenX, screenY);
298             }
299             endDrag();
300             break;
301         case MotionEvent.ACTION_CANCEL:
302             cancelDrag();
303         }
304
305         return true;
306     }
307
308     private boolean drop(float x, float y) {
309         final int[] coordinates = mCoordinatesTemp;
310         final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
311         if (dropTarget != null) {
312             dropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo);
313             if (dropTarget.acceptDrop(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo)) {
314                 dropTarget.onDrop(mDragSource, coordinates[0], coordinates[1], (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo);
315                 mDragSource.onDropCompleted((View)dropTarget, true);
316             }
317             else {
318                 mDragSource.onDropCompleted((View)dropTarget, false);
319             }
320             return true;
321         }
322         return false;
323     }
324     private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
325         final Rect r = mRectTemp;
326         final ArrayList<DropTarget> dropTargets = mDropTargets;
327         final int count = dropTargets.size();
328         for (int i = count - 1; i >= 0; i--) {
329             final DropTarget target = dropTargets.get(i);
330             target.getHitRect(r);
331             target.getLocationOnScreen(dropCoordinates);
332             r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
333             if (r.contains(x, y)) {
334                 dropCoordinates[0] = x - dropCoordinates[0];
335                 dropCoordinates[1] = y - dropCoordinates[1];
336                 return target;
337             }
338         }
339         return null;
340     }
341
342     private void recordScreenSize() {
343         ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(mDisplayMetrics);
344     }
345     private static int clamp(int val, int min, int max) {
346         if (val < min) {
347             return min;
348         }
349         else if (val >= max) {
350             return max - 1;
351         }
352         else {
353             return val;
354         }
355     }
356
357     public void setDragListener(DragListener listener) {
358         mListener = listener;
359     }
360     public void addDropTarget(DropTarget target) {
361         mDropTargets.add(target);
362     }
363     public void removeDropTarget(DropTarget target) {
364         mDropTargets.remove(target);
365     }
366 }