Home | History | Annotate | Download | only in launcher2
      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.launcher2;
     18 
     19 import android.content.Context;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Point;
     22 import android.graphics.Rect;
     23 import android.graphics.RectF;
     24 import android.os.Handler;
     25 import android.os.IBinder;
     26 import android.os.Vibrator;
     27 import android.util.Log;
     28 import android.view.KeyEvent;
     29 import android.view.MotionEvent;
     30 import android.view.View;
     31 import android.view.ViewConfiguration;
     32 import android.view.inputmethod.InputMethodManager;
     33 
     34 import com.android.launcher.R;
     35 
     36 import java.util.ArrayList;
     37 
     38 /**
     39  * Class for initiating a drag within a view or across multiple views.
     40  */
     41 public class DragController {
     42     @SuppressWarnings({"UnusedDeclaration"})
     43     private static final String TAG = "Launcher.DragController";
     44 
     45     /** Indicates the drag is a move.  */
     46     public static int DRAG_ACTION_MOVE = 0;
     47 
     48     /** Indicates the drag is a copy.  */
     49     public static int DRAG_ACTION_COPY = 1;
     50 
     51     private static final int SCROLL_DELAY = 600;
     52     private static final int VIBRATE_DURATION = 35;
     53 
     54     private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
     55 
     56     private static final int SCROLL_OUTSIDE_ZONE = 0;
     57     private static final int SCROLL_WAITING_IN_ZONE = 1;
     58 
     59     static final int SCROLL_NONE = -1;
     60     static final int SCROLL_LEFT = 0;
     61     static final int SCROLL_RIGHT = 1;
     62 
     63     private Launcher mLauncher;
     64     private Handler mHandler;
     65     private final Vibrator mVibrator = new Vibrator();
     66 
     67     // temporaries to avoid gc thrash
     68     private Rect mRectTemp = new Rect();
     69     private final int[] mCoordinatesTemp = new int[2];
     70 
     71     /** Whether or not we're dragging. */
     72     private boolean mDragging;
     73 
     74     /** X coordinate of the down event. */
     75     private int mMotionDownX;
     76 
     77     /** Y coordinate of the down event. */
     78     private int mMotionDownY;
     79 
     80     /** the area at the edge of the screen that makes the workspace go left
     81      *   or right while you're dragging.
     82      */
     83     private int mScrollZone;
     84 
     85     private DropTarget.DragObject mDragObject;
     86 
     87     /** Who can receive drop events */
     88     private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
     89 
     90     private ArrayList<DragListener> mListeners = new ArrayList<DragListener>();
     91 
     92     /** The window token used as the parent for the DragView. */
     93     private IBinder mWindowToken;
     94 
     95     /** The view that will be scrolled when dragging to the left and right edges of the screen. */
     96     private View mScrollView;
     97 
     98     private View mMoveTarget;
     99 
    100     private DragScroller mDragScroller;
    101     private int mScrollState = SCROLL_OUTSIDE_ZONE;
    102     private ScrollRunnable mScrollRunnable = new ScrollRunnable();
    103 
    104     private RectF mDeleteRegion;
    105     private DropTarget mLastDropTarget;
    106 
    107     private InputMethodManager mInputMethodManager;
    108 
    109     private int mLastTouch[] = new int[2];
    110     private int mDistanceSinceScroll = 0;
    111 
    112     private int mTmpPoint[] = new int[2];
    113     private Rect mDragLayerRect = new Rect();
    114 
    115     /**
    116      * Interface to receive notifications when a drag starts or stops
    117      */
    118     interface DragListener {
    119 
    120         /**
    121          * A drag has begun
    122          *
    123          * @param source An object representing where the drag originated
    124          * @param info The data associated with the object that is being dragged
    125          * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}
    126          *        or {@link DragController#DRAG_ACTION_COPY}
    127          */
    128         void onDragStart(DragSource source, Object info, int dragAction);
    129 
    130         /**
    131          * The drag has ended
    132          */
    133         void onDragEnd();
    134     }
    135 
    136     /**
    137      * Used to create a new DragLayer from XML.
    138      *
    139      * @param context The application's context.
    140      */
    141     public DragController(Launcher launcher) {
    142         mLauncher = launcher;
    143         mHandler = new Handler();
    144         mScrollZone = launcher.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
    145     }
    146 
    147     public boolean dragging() {
    148         return mDragging;
    149     }
    150 
    151     /**
    152      * Starts a drag.
    153      *
    154      * @param v The view that is being dragged
    155      * @param source An object representing where the drag originated
    156      * @param dragInfo The data associated with the object that is being dragged
    157      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
    158      *        {@link #DRAG_ACTION_COPY}
    159      */
    160     public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {
    161         startDrag(v, source, dragInfo, dragAction, null);
    162     }
    163 
    164     /**
    165      * Starts a drag.
    166      *
    167      * @param v The view that is being dragged
    168      * @param source An object representing where the drag originated
    169      * @param dragInfo The data associated with the object that is being dragged
    170      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
    171      *        {@link #DRAG_ACTION_COPY}
    172      * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
    173      *          Makes dragging feel more precise, e.g. you can clip out a transparent border
    174      */
    175     public void startDrag(View v, DragSource source, Object dragInfo, int dragAction,
    176             Rect dragRegion) {
    177         Bitmap b = getViewBitmap(v);
    178 
    179         if (b == null) {
    180             // out of memory?
    181             return;
    182         }
    183 
    184         int[] loc = mCoordinatesTemp;
    185         mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
    186         int dragLayerX = loc[0];
    187         int dragLayerY = loc[1];
    188 
    189         startDrag(b, dragLayerX, dragLayerY, source, dragInfo, dragAction, null, dragRegion);
    190         b.recycle();
    191 
    192         if (dragAction == DRAG_ACTION_MOVE) {
    193             v.setVisibility(View.GONE);
    194         }
    195     }
    196 
    197     /**
    198      * Starts a drag.
    199      *
    200      * @param v The view that is being dragged
    201      * @param bmp The bitmap that represents the view being dragged
    202      * @param source An object representing where the drag originated
    203      * @param dragInfo The data associated with the object that is being dragged
    204      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
    205      *        {@link #DRAG_ACTION_COPY}
    206      * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
    207      *          Makes dragging feel more precise, e.g. you can clip out a transparent border
    208      */
    209     public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, int dragAction,
    210             Rect dragRegion) {
    211         int[] loc = mCoordinatesTemp;
    212         mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
    213         int dragLayerX = loc[0];
    214         int dragLayerY = loc[1];
    215 
    216         startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null, dragRegion);
    217 
    218         if (dragAction == DRAG_ACTION_MOVE) {
    219             v.setVisibility(View.GONE);
    220         }
    221     }
    222 
    223     /**
    224      * Starts a drag.
    225      *
    226      * @param b The bitmap to display as the drag image.  It will be re-scaled to the
    227      *          enlarged size.
    228      * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
    229      * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
    230      * @param source An object representing where the drag originated
    231      * @param dragInfo The data associated with the object that is being dragged
    232      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
    233      *        {@link #DRAG_ACTION_COPY}
    234      */
    235     public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
    236             DragSource source, Object dragInfo, int dragAction) {
    237         startDrag(b, dragLayerX, dragLayerY, source, dragInfo, dragAction, null, null);
    238     }
    239 
    240     /**
    241      * Starts a drag.
    242      *
    243      * @param b The bitmap to display as the drag image.  It will be re-scaled to the
    244      *          enlarged size.
    245      * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
    246      * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
    247      * @param source An object representing where the drag originated
    248      * @param dragInfo The data associated with the object that is being dragged
    249      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
    250      *        {@link #DRAG_ACTION_COPY}
    251      * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
    252      *          Makes dragging feel more precise, e.g. you can clip out a transparent border
    253      */
    254     public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
    255             DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion) {
    256         if (PROFILE_DRAWING_DURING_DRAG) {
    257             android.os.Debug.startMethodTracing("Launcher");
    258         }
    259 
    260         // Hide soft keyboard, if visible
    261         if (mInputMethodManager == null) {
    262             mInputMethodManager = (InputMethodManager)
    263                     mLauncher.getSystemService(Context.INPUT_METHOD_SERVICE);
    264         }
    265         mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
    266 
    267         for (DragListener listener : mListeners) {
    268             listener.onDragStart(source, dragInfo, dragAction);
    269         }
    270 
    271         final int registrationX = mMotionDownX - dragLayerX;
    272         final int registrationY = mMotionDownY - dragLayerY;
    273 
    274         final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
    275         final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
    276 
    277         mDragging = true;
    278 
    279         mDragObject = new DropTarget.DragObject();
    280 
    281         mDragObject.dragComplete = false;
    282         mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
    283         mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
    284         mDragObject.dragSource = source;
    285         mDragObject.dragInfo = dragInfo;
    286 
    287         mVibrator.vibrate(VIBRATE_DURATION);
    288 
    289         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
    290                 registrationY, 0, 0, b.getWidth(), b.getHeight());
    291 
    292         if (dragOffset != null) {
    293             dragView.setDragVisualizeOffset(new Point(dragOffset));
    294         }
    295         if (dragRegion != null) {
    296             dragView.setDragRegion(new Rect(dragRegion));
    297         }
    298 
    299         dragView.show(mMotionDownX, mMotionDownY);
    300         handleMoveEvent(mMotionDownX, mMotionDownY);
    301     }
    302 
    303     /**
    304      * Draw the view into a bitmap.
    305      */
    306     Bitmap getViewBitmap(View v) {
    307         v.clearFocus();
    308         v.setPressed(false);
    309 
    310         boolean willNotCache = v.willNotCacheDrawing();
    311         v.setWillNotCacheDrawing(false);
    312 
    313         // Reset the drawing cache background color to fully transparent
    314         // for the duration of this operation
    315         int color = v.getDrawingCacheBackgroundColor();
    316         v.setDrawingCacheBackgroundColor(0);
    317         float alpha = v.getAlpha();
    318         v.setAlpha(1.0f);
    319 
    320         if (color != 0) {
    321             v.destroyDrawingCache();
    322         }
    323         v.buildDrawingCache();
    324         Bitmap cacheBitmap = v.getDrawingCache();
    325         if (cacheBitmap == null) {
    326             Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
    327             return null;
    328         }
    329 
    330         Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
    331 
    332         // Restore the view
    333         v.destroyDrawingCache();
    334         v.setAlpha(alpha);
    335         v.setWillNotCacheDrawing(willNotCache);
    336         v.setDrawingCacheBackgroundColor(color);
    337 
    338         return bitmap;
    339     }
    340 
    341     /**
    342      * Call this from a drag source view like this:
    343      *
    344      * <pre>
    345      *  @Override
    346      *  public boolean dispatchKeyEvent(KeyEvent event) {
    347      *      return mDragController.dispatchKeyEvent(this, event)
    348      *              || super.dispatchKeyEvent(event);
    349      * </pre>
    350      */
    351     @SuppressWarnings({"UnusedDeclaration"})
    352     public boolean dispatchKeyEvent(KeyEvent event) {
    353         return mDragging;
    354     }
    355 
    356     public boolean isDragging() {
    357         return mDragging;
    358     }
    359 
    360     /**
    361      * Stop dragging without dropping.
    362      */
    363     public void cancelDrag() {
    364         if (mDragging) {
    365             if (mLastDropTarget != null) {
    366                 mLastDropTarget.onDragExit(mDragObject);
    367             }
    368             mDragObject.cancelled = true;
    369             mDragObject.dragComplete = true;
    370             mDragObject.dragSource.onDropCompleted(null, mDragObject, false);
    371         }
    372         endDrag();
    373     }
    374     public void onAppsRemoved(ArrayList<ApplicationInfo> apps, Context context) {
    375         // Cancel the current drag if we are removing an app that we are dragging
    376         if (mDragObject != null) {
    377             Object rawDragInfo = mDragObject.dragInfo;
    378             if (rawDragInfo instanceof ShortcutInfo) {
    379                 ShortcutInfo dragInfo = (ShortcutInfo) rawDragInfo;
    380                 for (ApplicationInfo info : apps) {
    381                     if (dragInfo.intent.getComponent().equals(info.intent.getComponent())) {
    382                         cancelDrag();
    383                         return;
    384                     }
    385                 }
    386             }
    387         }
    388     }
    389 
    390     private void endDrag() {
    391         if (mDragging) {
    392             mDragging = false;
    393             for (DragListener listener : mListeners) {
    394                 listener.onDragEnd();
    395             }
    396             if (mDragObject.dragView != null) {
    397                 mDragObject.dragView.remove();
    398                 mDragObject.dragView = null;
    399             }
    400         }
    401     }
    402 
    403     /**
    404      * Clamps the position to the drag layer bounds.
    405      */
    406     private int[] getClampedDragLayerPos(float x, float y) {
    407         mLauncher.getDragLayer().getLocalVisibleRect(mDragLayerRect);
    408         mTmpPoint[0] = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1));
    409         mTmpPoint[1] = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1));
    410         return mTmpPoint;
    411     }
    412 
    413     /**
    414      * Call this from a drag source view.
    415      */
    416     public boolean onInterceptTouchEvent(MotionEvent ev) {
    417         if (false) {
    418             Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
    419                     + mDragging);
    420         }
    421         final int action = ev.getAction();
    422 
    423         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
    424         final int dragLayerX = dragLayerPos[0];
    425         final int dragLayerY = dragLayerPos[1];
    426 
    427         switch (action) {
    428             case MotionEvent.ACTION_MOVE:
    429                 break;
    430             case MotionEvent.ACTION_DOWN:
    431                 // Remember location of down touch
    432                 mMotionDownX = dragLayerX;
    433                 mMotionDownY = dragLayerY;
    434                 mLastDropTarget = null;
    435                 break;
    436             case MotionEvent.ACTION_UP:
    437                 if (mDragging) {
    438                     drop(dragLayerX, dragLayerY);
    439                 }
    440                 endDrag();
    441                 break;
    442             case MotionEvent.ACTION_CANCEL:
    443                 cancelDrag();
    444                 break;
    445         }
    446 
    447         return mDragging;
    448     }
    449 
    450     /**
    451      * Sets the view that should handle move events.
    452      */
    453     void setMoveTarget(View view) {
    454         mMoveTarget = view;
    455     }
    456 
    457     public boolean dispatchUnhandledMove(View focused, int direction) {
    458         return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
    459     }
    460 
    461     private void handleMoveEvent(int x, int y) {
    462         mDragObject.dragView.move(x, y);
    463 
    464         // Drop on someone?
    465         final int[] coordinates = mCoordinatesTemp;
    466         DropTarget dropTarget = findDropTarget(x, y, coordinates);
    467         mDragObject.x = coordinates[0];
    468         mDragObject.y = coordinates[1];
    469         if (dropTarget != null) {
    470             DropTarget delegate = dropTarget.getDropTargetDelegate(mDragObject);
    471             if (delegate != null) {
    472                 dropTarget = delegate;
    473             }
    474 
    475             if (mLastDropTarget != dropTarget) {
    476                 if (mLastDropTarget != null) {
    477                     mLastDropTarget.onDragExit(mDragObject);
    478                 }
    479                 dropTarget.onDragEnter(mDragObject);
    480             }
    481             dropTarget.onDragOver(mDragObject);
    482         } else {
    483             if (mLastDropTarget != null) {
    484                 mLastDropTarget.onDragExit(mDragObject);
    485             }
    486         }
    487         mLastDropTarget = dropTarget;
    488 
    489         // Scroll, maybe, but not if we're in the delete region.
    490         boolean inDeleteRegion = false;
    491         if (mDeleteRegion != null) {
    492             inDeleteRegion = mDeleteRegion.contains(x, y);
    493         }
    494 
    495         // After a scroll, the touch point will still be in the scroll region.
    496         // Rather than scrolling immediately, require a bit of twiddling to scroll again
    497         final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
    498         mDistanceSinceScroll +=
    499             Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2));
    500         mLastTouch[0] = x;
    501         mLastTouch[1] = y;
    502 
    503         if (!inDeleteRegion && x < mScrollZone) {
    504             if (mScrollState == SCROLL_OUTSIDE_ZONE && mDistanceSinceScroll > slop) {
    505                 mScrollState = SCROLL_WAITING_IN_ZONE;
    506                 if (mDragScroller.onEnterScrollArea(x, y, SCROLL_LEFT)) {
    507                     mScrollRunnable.setDirection(SCROLL_LEFT);
    508                     mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
    509                 }
    510             }
    511         } else if (!inDeleteRegion && x > mScrollView.getWidth() - mScrollZone) {
    512             if (mScrollState == SCROLL_OUTSIDE_ZONE && mDistanceSinceScroll > slop) {
    513                 mScrollState = SCROLL_WAITING_IN_ZONE;
    514                 if (mDragScroller.onEnterScrollArea(x, y, SCROLL_RIGHT)) {
    515                     mScrollRunnable.setDirection(SCROLL_RIGHT);
    516                     mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
    517                 }
    518             }
    519         } else {
    520             if (mScrollState == SCROLL_WAITING_IN_ZONE) {
    521                 mScrollState = SCROLL_OUTSIDE_ZONE;
    522                 mScrollRunnable.setDirection(SCROLL_RIGHT);
    523                 mHandler.removeCallbacks(mScrollRunnable);
    524                 mDragScroller.onExitScrollArea();
    525             }
    526         }
    527     }
    528 
    529     /**
    530      * Call this from a drag source view.
    531      */
    532     public boolean onTouchEvent(MotionEvent ev) {
    533         if (!mDragging) {
    534             return false;
    535         }
    536 
    537         final int action = ev.getAction();
    538         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
    539         final int dragLayerX = dragLayerPos[0];
    540         final int dragLayerY = dragLayerPos[1];
    541 
    542         switch (action) {
    543         case MotionEvent.ACTION_DOWN:
    544             // Remember where the motion event started
    545             mMotionDownX = dragLayerX;
    546             mMotionDownY = dragLayerY;
    547 
    548             if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
    549                 mScrollState = SCROLL_WAITING_IN_ZONE;
    550                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
    551             } else {
    552                 mScrollState = SCROLL_OUTSIDE_ZONE;
    553             }
    554             break;
    555         case MotionEvent.ACTION_MOVE:
    556             handleMoveEvent(dragLayerX, dragLayerY);
    557             break;
    558         case MotionEvent.ACTION_UP:
    559             // Ensure that we've processed a move event at the current pointer location.
    560             handleMoveEvent(dragLayerX, dragLayerY);
    561 
    562             mHandler.removeCallbacks(mScrollRunnable);
    563             if (mDragging) {
    564                 drop(dragLayerX, dragLayerY);
    565             }
    566             endDrag();
    567             break;
    568         case MotionEvent.ACTION_CANCEL:
    569             cancelDrag();
    570             break;
    571         }
    572 
    573         return true;
    574     }
    575 
    576     private void drop(float x, float y) {
    577         final int[] coordinates = mCoordinatesTemp;
    578         final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
    579 
    580         mDragObject.x = coordinates[0];
    581         mDragObject.y = coordinates[1];
    582         boolean accepted = false;
    583         if (dropTarget != null) {
    584             mDragObject.dragComplete = true;
    585             dropTarget.onDragExit(mDragObject);
    586             if (dropTarget.acceptDrop(mDragObject)) {
    587                 dropTarget.onDrop(mDragObject);
    588                 accepted = true;
    589             }
    590         }
    591         mDragObject.dragSource.onDropCompleted((View) dropTarget, mDragObject, accepted);
    592     }
    593 
    594     private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
    595         final Rect r = mRectTemp;
    596 
    597         final ArrayList<DropTarget> dropTargets = mDropTargets;
    598         final int count = dropTargets.size();
    599         for (int i=count-1; i>=0; i--) {
    600             DropTarget target = dropTargets.get(i);
    601             if (!target.isDropEnabled())
    602                 continue;
    603 
    604             target.getHitRect(r);
    605 
    606             // Convert the hit rect to DragLayer coordinates
    607             target.getLocationInDragLayer(dropCoordinates);
    608             r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
    609 
    610             mDragObject.x = x;
    611             mDragObject.y = y;
    612             if (r.contains(x, y)) {
    613                 DropTarget delegate = target.getDropTargetDelegate(mDragObject);
    614                 if (delegate != null) {
    615                     target = delegate;
    616                     target.getLocationInDragLayer(dropCoordinates);
    617                 }
    618 
    619                 // Make dropCoordinates relative to the DropTarget
    620                 dropCoordinates[0] = x - dropCoordinates[0];
    621                 dropCoordinates[1] = y - dropCoordinates[1];
    622 
    623                 return target;
    624             }
    625         }
    626         return null;
    627     }
    628 
    629     public void setDragScoller(DragScroller scroller) {
    630         mDragScroller = scroller;
    631     }
    632 
    633     public void setWindowToken(IBinder token) {
    634         mWindowToken = token;
    635     }
    636 
    637     /**
    638      * Sets the drag listner which will be notified when a drag starts or ends.
    639      */
    640     public void addDragListener(DragListener l) {
    641         mListeners.add(l);
    642     }
    643 
    644     /**
    645      * Remove a previously installed drag listener.
    646      */
    647     public void removeDragListener(DragListener l) {
    648         mListeners.remove(l);
    649     }
    650 
    651     /**
    652      * Add a DropTarget to the list of potential places to receive drop events.
    653      */
    654     public void addDropTarget(DropTarget target) {
    655         mDropTargets.add(target);
    656     }
    657 
    658     /**
    659      * Don't send drop events to <em>target</em> any more.
    660      */
    661     public void removeDropTarget(DropTarget target) {
    662         mDropTargets.remove(target);
    663     }
    664 
    665     /**
    666      * Set which view scrolls for touch events near the edge of the screen.
    667      */
    668     public void setScrollView(View v) {
    669         mScrollView = v;
    670     }
    671 
    672     /**
    673      * Specifies the delete region.  We won't scroll on touch events over the delete region.
    674      *
    675      * @param region The rectangle in DragLayer coordinates of the delete region.
    676      */
    677     void setDeleteRegion(RectF region) {
    678         mDeleteRegion = region;
    679     }
    680 
    681     DragView getDragView() {
    682         return mDragObject.dragView;
    683     }
    684 
    685     private class ScrollRunnable implements Runnable {
    686         private int mDirection;
    687 
    688         ScrollRunnable() {
    689         }
    690 
    691         public void run() {
    692             if (mDragScroller != null) {
    693                 if (mDirection == SCROLL_LEFT) {
    694                     mDragScroller.scrollLeft();
    695                 } else {
    696                     mDragScroller.scrollRight();
    697                 }
    698                 mScrollState = SCROLL_OUTSIDE_ZONE;
    699                 mDistanceSinceScroll = 0;
    700                 mDragScroller.onExitScrollArea();
    701             }
    702         }
    703 
    704         void setDirection(int direction) {
    705             mDirection = direction;
    706         }
    707     }
    708 }
    709