summaryrefslogtreecommitdiff
path: root/android/view/DragEvent.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/view/DragEvent.java')
-rw-r--r--android/view/DragEvent.java551
1 files changed, 551 insertions, 0 deletions
diff --git a/android/view/DragEvent.java b/android/view/DragEvent.java
new file mode 100644
index 00000000..16f2d7d0
--- /dev/null
+++ b/android/view/DragEvent.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.view.IDragAndDropPermissions;
+
+//TODO: Improve Javadoc
+/**
+ * Represents an event that is sent out by the system at various times during a drag and drop
+ * operation. It is a data structure that contains several important pieces of data about
+ * the operation and the underlying data.
+ * <p>
+ * View objects that receive a DragEvent call {@link #getAction()}, which returns
+ * an action type that indicates the state of the drag and drop operation. This allows a View
+ * object to react to a change in state by changing its appearance or performing other actions.
+ * For example, a View can react to the {@link #ACTION_DRAG_ENTERED} action type by
+ * by changing one or more colors in its displayed image.
+ * </p>
+ * <p>
+ * During a drag and drop operation, the system displays an image that the user drags. This image
+ * is called a drag shadow. Several action types reflect the position of the drag shadow relative
+ * to the View receiving the event.
+ * </p>
+ * <p>
+ * Most methods return valid data only for certain event actions. This is summarized in the
+ * following table. Each possible {@link #getAction()} value is listed in the first column. The
+ * other columns indicate which method or methods return valid data for that getAction() value:
+ * </p>
+ * <table>
+ * <tr>
+ * <th scope="col">getAction() Value</th>
+ * <th scope="col">getClipDescription()</th>
+ * <th scope="col">getLocalState()</th>
+ * <th scope="col">getX()</th>
+ * <th scope="col">getY()</th>
+ * <th scope="col">getClipData()</th>
+ * <th scope="col">getResult()</th>
+ * </tr>
+ * <tr>
+ * <td>ACTION_DRAG_STARTED</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>ACTION_DRAG_ENTERED</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>ACTION_DRAG_LOCATION</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>ACTION_DRAG_EXITED</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>ACTION_DROP</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">X</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>ACTION_DRAG_ENDED</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">X</td>
+ * </tr>
+ * </table>
+ * <p>
+ * The {@link android.view.DragEvent#getAction()},
+ * {@link android.view.DragEvent#describeContents()},
+ * {@link android.view.DragEvent#writeToParcel(Parcel,int)}, and
+ * {@link android.view.DragEvent#toString()} methods always return valid data.
+ * </p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For a guide to implementing drag and drop features, read the
+ * <a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and Drop</a> developer guide.</p>
+ * </div>
+ */
+public class DragEvent implements Parcelable {
+ private static final boolean TRACK_RECYCLED_LOCATION = false;
+
+ int mAction;
+ float mX, mY;
+ ClipDescription mClipDescription;
+ ClipData mClipData;
+ IDragAndDropPermissions mDragAndDropPermissions;
+
+ Object mLocalState;
+ boolean mDragResult;
+ boolean mEventHandlerWasCalled;
+
+ private DragEvent mNext;
+ private RuntimeException mRecycledLocation;
+ private boolean mRecycled;
+
+ private static final int MAX_RECYCLED = 10;
+ private static final Object gRecyclerLock = new Object();
+ private static int gRecyclerUsed = 0;
+ private static DragEvent gRecyclerTop = null;
+
+ /**
+ * Action constant returned by {@link #getAction()}: Signals the start of a
+ * drag and drop operation. The View should return {@code true} from its
+ * {@link View#onDragEvent(DragEvent) onDragEvent()} handler method or
+ * {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener
+ * if it can accept a drop. The onDragEvent() or onDrag() methods usually inspect the metadata
+ * from {@link #getClipDescription()} to determine if they can accept the data contained in
+ * this drag. For an operation that doesn't represent data transfer, these methods may
+ * perform other actions to determine whether or not the View should accept the data.
+ * If the View wants to indicate that it is a valid drop target, it can also react by
+ * changing its appearance.
+ * <p>
+ * Views added or becoming visible for the first time during a drag operation receive this
+ * event when they are added or becoming visible.
+ * </p>
+ * <p>
+ * A View only receives further drag events for the drag operation if it returns {@code true}
+ * in response to ACTION_DRAG_STARTED.
+ * </p>
+ * @see #ACTION_DRAG_ENDED
+ * @see #getX()
+ * @see #getY()
+ */
+ public static final int ACTION_DRAG_STARTED = 1;
+
+ /**
+ * Action constant returned by {@link #getAction()}: Sent to a View after
+ * {@link #ACTION_DRAG_ENTERED} while the drag shadow is still within the View object's bounding
+ * box, but not within a descendant view that can accept the data. The {@link #getX()} and
+ * {@link #getY()} methods supply
+ * the X and Y position of of the drag point within the View object's bounding box.
+ * <p>
+ * A View receives an {@link #ACTION_DRAG_ENTERED} event before receiving any
+ * ACTION_DRAG_LOCATION events.
+ * </p>
+ * <p>
+ * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the
+ * drag shadow out of the View object's bounding box or into a descendant view that can accept
+ * the data. If the user moves the drag shadow back into the View object's bounding box or out
+ * of a descendant view that can accept the data, the View receives an ACTION_DRAG_ENTERED again
+ * before receiving any more ACTION_DRAG_LOCATION events.
+ * </p>
+ * @see #ACTION_DRAG_ENTERED
+ * @see #getX()
+ * @see #getY()
+ */
+ public static final int ACTION_DRAG_LOCATION = 2;
+
+ /**
+ * Action constant returned by {@link #getAction()}: Signals to a View that the user
+ * has released the drag shadow, and the drag point is within the bounding box of the View and
+ * not within a descendant view that can accept the data.
+ * The View should retrieve the data from the DragEvent by calling {@link #getClipData()}.
+ * The methods {@link #getX()} and {@link #getY()} return the X and Y position of the drop point
+ * within the View object's bounding box.
+ * <p>
+ * The View should return {@code true} from its {@link View#onDragEvent(DragEvent)}
+ * handler or {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()}
+ * listener if it accepted the drop, and {@code false} if it ignored the drop.
+ * </p>
+ * <p>
+ * The View can also react to this action by changing its appearance.
+ * </p>
+ * @see #getClipData()
+ * @see #getX()
+ * @see #getY()
+ */
+ public static final int ACTION_DROP = 3;
+
+ /**
+ * Action constant returned by {@link #getAction()}: Signals to a View that the drag and drop
+ * operation has concluded. A View that changed its appearance during the operation should
+ * return to its usual drawing state in response to this event.
+ * <p>
+ * All views with listeners that returned boolean <code>true</code> for the ACTION_DRAG_STARTED
+ * event will receive the ACTION_DRAG_ENDED event even if they are not currently visible when
+ * the drag ends. Views removed during the drag operation won't receive the ACTION_DRAG_ENDED
+ * event.
+ * </p>
+ * <p>
+ * The View object can call {@link #getResult()} to see the result of the operation.
+ * If a View returned {@code true} in response to {@link #ACTION_DROP}, then
+ * getResult() returns {@code true}, otherwise it returns {@code false}.
+ * </p>
+ * @see #ACTION_DRAG_STARTED
+ * @see #getResult()
+ */
+ public static final int ACTION_DRAG_ENDED = 4;
+
+ /**
+ * Action constant returned by {@link #getAction()}: Signals to a View that the drag point has
+ * entered the bounding box of the View.
+ * <p>
+ * If the View can accept a drop, it can react to ACTION_DRAG_ENTERED
+ * by changing its appearance in a way that tells the user that the View is the current
+ * drop target.
+ * </p>
+ * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the
+ * drag shadow out of the View object's bounding box or into a descendant view that can accept
+ * the data. If the user moves the drag shadow back into the View object's bounding box or out
+ * of a descendant view that can accept the data, the View receives an ACTION_DRAG_ENTERED again
+ * before receiving any more ACTION_DRAG_LOCATION events.
+ * </p>
+ * @see #ACTION_DRAG_ENTERED
+ * @see #ACTION_DRAG_LOCATION
+ */
+ public static final int ACTION_DRAG_ENTERED = 5;
+
+ /**
+ * Action constant returned by {@link #getAction()}: Signals that the user has moved the
+ * drag shadow out of the bounding box of the View or into a descendant view that can accept
+ * the data.
+ * The View can react by changing its appearance in a way that tells the user that
+ * View is no longer the immediate drop target.
+ * <p>
+ * After the system sends an ACTION_DRAG_EXITED event to the View, the View receives no more
+ * ACTION_DRAG_LOCATION events until the user drags the drag shadow back over the View.
+ * </p>
+ *
+ */
+ public static final int ACTION_DRAG_EXITED = 6;
+
+ private DragEvent() {
+ }
+
+ private void init(int action, float x, float y, ClipDescription description, ClipData data,
+ IDragAndDropPermissions dragAndDropPermissions, Object localState, boolean result) {
+ mAction = action;
+ mX = x;
+ mY = y;
+ mClipDescription = description;
+ mClipData = data;
+ this.mDragAndDropPermissions = dragAndDropPermissions;
+ mLocalState = localState;
+ mDragResult = result;
+ }
+
+ static DragEvent obtain() {
+ return DragEvent.obtain(0, 0f, 0f, null, null, null, null, false);
+ }
+
+ /** @hide */
+ public static DragEvent obtain(int action, float x, float y, Object localState,
+ ClipDescription description, ClipData data,
+ IDragAndDropPermissions dragAndDropPermissions, boolean result) {
+ final DragEvent ev;
+ synchronized (gRecyclerLock) {
+ if (gRecyclerTop == null) {
+ ev = new DragEvent();
+ ev.init(action, x, y, description, data, dragAndDropPermissions, localState,
+ result);
+ return ev;
+ }
+ ev = gRecyclerTop;
+ gRecyclerTop = ev.mNext;
+ gRecyclerUsed -= 1;
+ }
+ ev.mRecycledLocation = null;
+ ev.mRecycled = false;
+ ev.mNext = null;
+
+ ev.init(action, x, y, description, data, dragAndDropPermissions, localState, result);
+
+ return ev;
+ }
+
+ /** @hide */
+ public static DragEvent obtain(DragEvent source) {
+ return obtain(source.mAction, source.mX, source.mY, source.mLocalState,
+ source.mClipDescription, source.mClipData, source.mDragAndDropPermissions,
+ source.mDragResult);
+ }
+
+ /**
+ * Inspect the action value of this event.
+ * @return One of the following action constants, in the order in which they usually occur
+ * during a drag and drop operation:
+ * <ul>
+ * <li>{@link #ACTION_DRAG_STARTED}</li>
+ * <li>{@link #ACTION_DRAG_ENTERED}</li>
+ * <li>{@link #ACTION_DRAG_LOCATION}</li>
+ * <li>{@link #ACTION_DROP}</li>
+ * <li>{@link #ACTION_DRAG_EXITED}</li>
+ * <li>{@link #ACTION_DRAG_ENDED}</li>
+ * </ul>
+ */
+ public int getAction() {
+ return mAction;
+ }
+
+ /**
+ * Gets the X coordinate of the drag point. The value is only valid if the event action is
+ * {@link #ACTION_DRAG_STARTED}, {@link #ACTION_DRAG_LOCATION} or {@link #ACTION_DROP}.
+ * @return The current drag point's X coordinate
+ */
+ public float getX() {
+ return mX;
+ }
+
+ /**
+ * Gets the Y coordinate of the drag point. The value is only valid if the event action is
+ * {@link #ACTION_DRAG_STARTED}, {@link #ACTION_DRAG_LOCATION} or {@link #ACTION_DROP}.
+ * @return The current drag point's Y coordinate
+ */
+ public float getY() {
+ return mY;
+ }
+
+ /**
+ * Returns the {@link android.content.ClipData} object sent to the system as part of the call
+ * to
+ * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
+ * This method only returns valid data if the event action is {@link #ACTION_DROP}.
+ * @return The ClipData sent to the system by startDragAndDrop().
+ */
+ public ClipData getClipData() {
+ return mClipData;
+ }
+
+ /**
+ * Returns the {@link android.content.ClipDescription} object contained in the
+ * {@link android.content.ClipData} object sent to the system as part of the call to
+ * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
+ * The drag handler or listener for a View can use the metadata in this object to decide if the
+ * View can accept the dragged View object's data.
+ * <p>
+ * This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
+ * @return The ClipDescription that was part of the ClipData sent to the system by
+ * startDragAndDrop().
+ */
+ public ClipDescription getClipDescription() {
+ return mClipDescription;
+ }
+
+ /** @hide */
+ public IDragAndDropPermissions getDragAndDropPermissions() {
+ return mDragAndDropPermissions;
+ }
+
+ /**
+ * Returns the local state object sent to the system as part of the call to
+ * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
+ * The object is intended to provide local information about the drag and drop operation. For
+ * example, it can indicate whether the drag and drop operation is a copy or a move.
+ * <p>
+ * The local state is available only to views in the activity which has started the drag
+ * operation. In all other activities this method will return null
+ * </p>
+ * <p>
+ * This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
+ * </p>
+ * @return The local state object sent to the system by startDragAndDrop().
+ */
+ public Object getLocalState() {
+ return mLocalState;
+ }
+
+ /**
+ * <p>
+ * Returns an indication of the result of the drag and drop operation.
+ * This method only returns valid data if the action type is {@link #ACTION_DRAG_ENDED}.
+ * The return value depends on what happens after the user releases the drag shadow.
+ * </p>
+ * <p>
+ * If the user releases the drag shadow on a View that can accept a drop, the system sends an
+ * {@link #ACTION_DROP} event to the View object's drag event listener. If the listener
+ * returns {@code true}, then getResult() will return {@code true}.
+ * If the listener returns {@code false}, then getResult() returns {@code false}.
+ * </p>
+ * <p>
+ * Notice that getResult() also returns {@code false} if no {@link #ACTION_DROP} is sent. This
+ * happens, for example, when the user releases the drag shadow over an area outside of the
+ * application. In this case, the system sends out {@link #ACTION_DRAG_ENDED} for the current
+ * operation, but never sends out {@link #ACTION_DROP}.
+ * </p>
+ * @return {@code true} if a drag event listener returned {@code true} in response to
+ * {@link #ACTION_DROP}. If the system did not send {@link #ACTION_DROP} before
+ * {@link #ACTION_DRAG_ENDED}, or if the listener returned {@code false} in response to
+ * {@link #ACTION_DROP}, then {@code false} is returned.
+ */
+ public boolean getResult() {
+ return mDragResult;
+ }
+
+ /**
+ * Recycle the DragEvent, to be re-used by a later caller. After calling
+ * this function you must never touch the event again.
+ *
+ * @hide
+ */
+ public final void recycle() {
+ // Ensure recycle is only called once!
+ if (TRACK_RECYCLED_LOCATION) {
+ if (mRecycledLocation != null) {
+ throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
+ }
+ mRecycledLocation = new RuntimeException("Last recycled here");
+ } else {
+ if (mRecycled) {
+ throw new RuntimeException(toString() + " recycled twice!");
+ }
+ mRecycled = true;
+ }
+
+ mClipData = null;
+ mClipDescription = null;
+ mLocalState = null;
+ mEventHandlerWasCalled = false;
+
+ synchronized (gRecyclerLock) {
+ if (gRecyclerUsed < MAX_RECYCLED) {
+ gRecyclerUsed++;
+ mNext = gRecyclerTop;
+ gRecyclerTop = this;
+ }
+ }
+ }
+
+ /**
+ * Returns a string containing a concise, human-readable representation of this DragEvent
+ * object.
+ * @return A string representation of the DragEvent object.
+ */
+ @Override
+ public String toString() {
+ return "DragEvent{" + Integer.toHexString(System.identityHashCode(this))
+ + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription
+ + " data=" + mClipData + " local=" + mLocalState + " result=" + mDragResult
+ + "}";
+ }
+
+ /* Parcelable interface */
+
+ /**
+ * Returns information about the {@link android.os.Parcel} representation of this DragEvent
+ * object.
+ * @return Information about the {@link android.os.Parcel} representation.
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Creates a {@link android.os.Parcel} object from this DragEvent object.
+ * @param dest A {@link android.os.Parcel} object in which to put the DragEvent object.
+ * @param flags Flags to store in the Parcel.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAction);
+ dest.writeFloat(mX);
+ dest.writeFloat(mY);
+ dest.writeInt(mDragResult ? 1 : 0);
+ if (mClipData == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mClipData.writeToParcel(dest, flags);
+ }
+ if (mClipDescription == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mClipDescription.writeToParcel(dest, flags);
+ }
+ if (mDragAndDropPermissions == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeStrongBinder(mDragAndDropPermissions.asBinder());
+ }
+ }
+
+ /**
+ * A container for creating a DragEvent from a Parcel.
+ */
+ public static final Parcelable.Creator<DragEvent> CREATOR =
+ new Parcelable.Creator<DragEvent>() {
+ public DragEvent createFromParcel(Parcel in) {
+ DragEvent event = DragEvent.obtain();
+ event.mAction = in.readInt();
+ event.mX = in.readFloat();
+ event.mY = in.readFloat();
+ event.mDragResult = (in.readInt() != 0);
+ if (in.readInt() != 0) {
+ event.mClipData = ClipData.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() != 0) {
+ event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() != 0) {
+ event.mDragAndDropPermissions =
+ IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());;
+ }
+ return event;
+ }
+
+ public DragEvent[] newArray(int size) {
+ return new DragEvent[size];
+ }
+ };
+}