DragEvent.java revision 855e4c98846a4264b7a27ad68cfa66c12ac0591c
1/*
2 * Copyright (C) 2010 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 android.view;
18
19import android.content.ClipData;
20import android.content.ClipDescription;
21import android.os.Parcel;
22import android.os.Parcelable;
23
24/** !!! TODO: real docs */
25public class DragEvent implements Parcelable {
26    private static final boolean TRACK_RECYCLED_LOCATION = false;
27
28    int mAction;
29    float mX, mY;
30    ClipDescription mClipDescription;
31    ClipData mClipData;
32    boolean mDragResult;
33
34    private DragEvent mNext;
35    private RuntimeException mRecycledLocation;
36    private boolean mRecycled;
37
38    private static final int MAX_RECYCLED = 10;
39    private static final Object gRecyclerLock = new Object();
40    private static int gRecyclerUsed = 0;
41    private static DragEvent gRecyclerTop = null;
42
43    /**
44     * Action constant returned by {@link #getAction()}.  Delivery of a DragEvent whose
45     * action is ACTION_DRAG_STARTED means that a drag operation has been initiated.  The
46     * view receiving this DragEvent should inspect the metadata of the dragged content,
47     * available via {@link #getClipDescription()}, and return {@code true} from
48     * {@link View#onDragEvent(DragEvent)} if the view is prepared to accept a drop of
49     * that clip data.  If the view chooses to present a visual indication that it is
50     * a valid target of the ongoing drag, then it should draw that indication in response
51     * to this event.
52     * <p>
53     * A view will only receive ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED,
54     * and ACTION_DRAG_LOCATION events if it returns {@code true} in response to the
55     * ACTION_DRAG_STARTED event.
56     */
57    public static final int ACTION_DRAG_STARTED = 1;
58
59    /**
60     * Action constant returned by {@link #getAction()}.  Delivery of a DragEvent whose
61     * action is ACTION_DRAG_LOCATION means that the drag operation is currently hovering
62     * over the view.  The {@link #getX()} and {@link #getY()} methods supply the location
63     * of the drag point within the view's coordinate system.
64     * <p>
65     * A view will receive an ACTION_DRAG_ENTERED event before receiving any
66     * ACTION_DRAG_LOCATION events.  If the drag point leaves the view, then an
67     * ACTION_DRAG_EXITED event is delivered to the view, after which no more
68     * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view,
69     * of course).
70     */
71    public static final int ACTION_DRAG_LOCATION = 2;
72
73    /**
74     * Action constant returned by {@link #getAction()}.  Delivery of a DragEvent whose
75     * action is ACTION_DROP means that the dragged content has been dropped on this view.
76     * The view should retrieve the content via {@link #getClipData()} and act on it
77     * appropriately.  The {@link #getX()} and {@link #getY()} methods supply the location
78     * of the drop point within the view's coordinate system.
79     * <p>
80     * The view should return {@code true} from its {@link View#onDragEvent(DragEvent)}
81     * method in response to this event if it accepted the content, and {@code false}
82     * if it ignored the drop.
83     */
84    public static final int ACTION_DROP = 3;
85
86    /**
87     * Action constant returned by {@link #getAction()}.  Delivery of a DragEvent whose
88     * action is ACTION_DRAG_ENDED means that the drag operation has concluded.  A view
89     * that is drawing a visual indication of drag acceptance should return to its usual
90     * drawing state in response to this event.
91     * <p>
92     * All views that received an ACTION_DRAG_STARTED event will receive the
93     * ACTION_DRAG_ENDED event even if they are not currently visible when the drag
94     * ends.
95     */
96    public static final int ACTION_DRAG_ENDED = 4;
97
98    /**
99     * Action constant returned by {@link #getAction()}.  Delivery of a DragEvent whose
100     * action is ACTION_DRAG_ENTERED means that the drag point has entered the view's
101     * bounds.  If the view changed its visual state in response to the ACTION_DRAG_ENTERED
102     * event, it should return to its normal drag-in-progress visual state in response to
103     * this event.
104     * <p>
105     * A view will receive an ACTION_DRAG_ENTERED event before receiving any
106     * ACTION_DRAG_LOCATION events.  If the drag point leaves the view, then an
107     * ACTION_DRAG_EXITED event is delivered to the view, after which no more
108     * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view,
109     * of course).
110     */
111    public static final int ACTION_DRAG_ENTERED = 5;
112
113    /**
114     * Action constant returned by {@link #getAction()}.  Delivery of a DragEvent whose
115     * action is ACTION_DRAG_ENTERED means that the drag point has entered the view's
116     * bounds.  If the view chooses to present a visual indication that it will receive
117     * the drop if it occurs now, then it should draw that indication in response to
118     * this event.
119     * <p>
120     * A view will receive an ACTION_DRAG_ENTERED event before receiving any
121     * ACTION_DRAG_LOCATION events.  If the drag point leaves the view, then an
122     * ACTION_DRAG_EXITED event is delivered to the view, after which no more
123     * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view,
124     * of course).
125     */
126public static final int ACTION_DRAG_EXITED = 6;
127
128    private DragEvent() {
129    }
130
131    private void init(int action, float x, float y, ClipDescription description, ClipData data,
132            boolean result) {
133        mAction = action;
134        mX = x;
135        mY = y;
136        mClipDescription = description;
137        mClipData = data;
138        mDragResult = result;
139    }
140
141    static DragEvent obtain() {
142        return DragEvent.obtain(0, 0f, 0f, null, null, false);
143    }
144
145    /** @hide */
146    public static DragEvent obtain(int action, float x, float y,
147            ClipDescription description, ClipData data, boolean result) {
148        final DragEvent ev;
149        synchronized (gRecyclerLock) {
150            if (gRecyclerTop == null) {
151                ev = new DragEvent();
152                ev.init(action, x, y, description, data, result);
153                return ev;
154            }
155            ev = gRecyclerTop;
156            gRecyclerTop = ev.mNext;
157            gRecyclerUsed -= 1;
158        }
159        ev.mRecycledLocation = null;
160        ev.mRecycled = false;
161        ev.mNext = null;
162
163        ev.init(action, x, y, description, data, result);
164
165        return ev;
166    }
167
168    /** @hide */
169    public static DragEvent obtain(DragEvent source) {
170        return obtain(source.mAction, source.mX, source.mY,
171                source.mClipDescription, source.mClipData, source.mDragResult);
172    }
173
174    /**
175     * Inspect the action value of this event.
176     * @return One of {@link #ACTION_DRAG_STARTED}, {@link #ACTION_DRAG_ENDED},
177     *         {@link #ACTION_DROP}, {@link #ACTION_DRAG_ENTERED}, {@link #ACTION_DRAG_EXITED},
178     *         or {@link #ACTION_DRAG_LOCATION}.
179     */
180    public int getAction() {
181        return mAction;
182    }
183
184    /**
185     * For ACTION_DRAG_LOCATION and ACTION_DROP events, returns the x coordinate of the
186     * drag point.
187     * @return The current drag point's x coordinate, when relevant.
188     */
189    public float getX() {
190        return mX;
191    }
192
193    /**
194     * For ACTION_DRAG_LOCATION and ACTION_DROP events, returns the y coordinate of the
195     * drag point.
196     * @return The current drag point's y coordinate, when relevant.
197     */
198    public float getY() {
199        return mY;
200    }
201
202    /**
203     * Provides the data payload of the drag operation.  This payload is only available
204     * for events whose action value is ACTION_DROP.
205     * @return The ClipData containing the data being dropped on the view.
206     */
207    public ClipData getClipData() {
208        return mClipData;
209    }
210
211    /**
212     * Provides a description of the drag operation's data payload.  This payload is
213     * available for all DragEvents other than ACTION_DROP.
214     * @return A ClipDescription describing the contents of the data being dragged.
215     */
216    public ClipDescription getClipDescription() {
217        return mClipDescription;
218    }
219
220    /**
221     * Provides an indication of whether the drag operation concluded successfully.
222     * This method is only available on ACTION_DRAG_ENDED events.
223     * @return {@code true} if the drag operation ended with an accepted drop; {@code false}
224     *         otherwise.
225     */
226    public boolean getResult() {
227        return mDragResult;
228    }
229
230    /**
231     * Recycle the DragEvent, to be re-used by a later caller.  After calling
232     * this function you must never touch the event again.
233     *
234     * @hide
235     */
236    public final void recycle() {
237        // Ensure recycle is only called once!
238        if (TRACK_RECYCLED_LOCATION) {
239            if (mRecycledLocation != null) {
240                throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
241            }
242            mRecycledLocation = new RuntimeException("Last recycled here");
243        } else {
244            if (mRecycled) {
245                throw new RuntimeException(toString() + " recycled twice!");
246            }
247            mRecycled = true;
248        }
249
250        mClipData = null;
251        mClipDescription = null;
252
253        synchronized (gRecyclerLock) {
254            if (gRecyclerUsed < MAX_RECYCLED) {
255                gRecyclerUsed++;
256                mNext = gRecyclerTop;
257                gRecyclerTop = this;
258            }
259        }
260    }
261
262    @Override
263    public String toString() {
264        return "DragEvent{" + Integer.toHexString(System.identityHashCode(this))
265        + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription
266        + " data=" + mClipData + " result=" + mDragResult
267        + "}";
268    }
269
270    /* Parcelable interface */
271
272    public int describeContents() {
273        return 0;
274    }
275
276    public void writeToParcel(Parcel dest, int flags) {
277        dest.writeInt(mAction);
278        dest.writeFloat(mX);
279        dest.writeFloat(mY);
280        dest.writeInt(mDragResult ? 1 : 0);
281        if (mClipData == null) {
282            dest.writeInt(0);
283        } else {
284            dest.writeInt(1);
285            mClipData.writeToParcel(dest, flags);
286        }
287        if (mClipDescription == null) {
288            dest.writeInt(0);
289        } else {
290            dest.writeInt(1);
291            mClipDescription.writeToParcel(dest, flags);
292        }
293    }
294
295    public static final Parcelable.Creator<DragEvent> CREATOR =
296        new Parcelable.Creator<DragEvent>() {
297        public DragEvent createFromParcel(Parcel in) {
298            DragEvent event = DragEvent.obtain();
299            event.mAction = in.readInt();
300            event.mX = in.readFloat();
301            event.mY = in.readFloat();
302            event.mDragResult = (in.readInt() != 0);
303            if (in.readInt() != 0) {
304                event.mClipData = ClipData.CREATOR.createFromParcel(in);
305            }
306            if (in.readInt() != 0) {
307                event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
308            }
309            return event;
310        }
311
312        public DragEvent[] newArray(int size) {
313            return new DragEvent[size];
314        }
315    };
316}
317