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