diff options
Diffstat (limited to 'android/view/DragEvent.java')
-rw-r--r-- | android/view/DragEvent.java | 551 |
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;"> </td> + * <td style="text-align: center;"> </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;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </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;"> </td> + * <td style="text-align: center;"> </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;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </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;"> </td> + * </tr> + * <tr> + * <td>ACTION_DRAG_ENDED</td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </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]; + } + }; +} |