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 17 package android.view; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.content.ClipData; 21 import android.content.ClipDescription; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 25 import com.android.internal.view.IDragAndDropPermissions; 26 27 //TODO: Improve Javadoc 28 /** 29 * Represents an event that is sent out by the system at various times during a drag and drop 30 * operation. It is a data structure that contains several important pieces of data about 31 * the operation and the underlying data. 32 * <p> 33 * View objects that receive a DragEvent call {@link #getAction()}, which returns 34 * an action type that indicates the state of the drag and drop operation. This allows a View 35 * object to react to a change in state by changing its appearance or performing other actions. 36 * For example, a View can react to the {@link #ACTION_DRAG_ENTERED} action type by 37 * by changing one or more colors in its displayed image. 38 * </p> 39 * <p> 40 * During a drag and drop operation, the system displays an image that the user drags. This image 41 * is called a drag shadow. Several action types reflect the position of the drag shadow relative 42 * to the View receiving the event. 43 * </p> 44 * <p> 45 * Most methods return valid data only for certain event actions. This is summarized in the 46 * following table. Each possible {@link #getAction()} value is listed in the first column. The 47 * other columns indicate which method or methods return valid data for that getAction() value: 48 * </p> 49 * <table> 50 * <tr> 51 * <th scope="col">getAction() Value</th> 52 * <th scope="col">getClipDescription()</th> 53 * <th scope="col">getLocalState()</th> 54 * <th scope="col">getX()</th> 55 * <th scope="col">getY()</th> 56 * <th scope="col">getClipData()</th> 57 * <th scope="col">getResult()</th> 58 * </tr> 59 * <tr> 60 * <td>ACTION_DRAG_STARTED</td> 61 * <td style="text-align: center;">X</td> 62 * <td style="text-align: center;">X</td> 63 * <td style="text-align: center;">X</td> 64 * <td style="text-align: center;">X</td> 65 * <td style="text-align: center;"> </td> 66 * <td style="text-align: center;"> </td> 67 * </tr> 68 * <tr> 69 * <td>ACTION_DRAG_ENTERED</td> 70 * <td style="text-align: center;">X</td> 71 * <td style="text-align: center;">X</td> 72 * <td style="text-align: center;"> </td> 73 * <td style="text-align: center;"> </td> 74 * <td style="text-align: center;"> </td> 75 * <td style="text-align: center;"> </td> 76 * </tr> 77 * <tr> 78 * <td>ACTION_DRAG_LOCATION</td> 79 * <td style="text-align: center;">X</td> 80 * <td style="text-align: center;">X</td> 81 * <td style="text-align: center;">X</td> 82 * <td style="text-align: center;">X</td> 83 * <td style="text-align: center;"> </td> 84 * <td style="text-align: center;"> </td> 85 * </tr> 86 * <tr> 87 * <td>ACTION_DRAG_EXITED</td> 88 * <td style="text-align: center;">X</td> 89 * <td style="text-align: center;">X</td> 90 * <td style="text-align: center;"> </td> 91 * <td style="text-align: center;"> </td> 92 * <td style="text-align: center;"> </td> 93 * <td style="text-align: center;"> </td> 94 * </tr> 95 * <tr> 96 * <td>ACTION_DROP</td> 97 * <td style="text-align: center;">X</td> 98 * <td style="text-align: center;">X</td> 99 * <td style="text-align: center;">X</td> 100 * <td style="text-align: center;">X</td> 101 * <td style="text-align: center;">X</td> 102 * <td style="text-align: center;"> </td> 103 * </tr> 104 * <tr> 105 * <td>ACTION_DRAG_ENDED</td> 106 * <td style="text-align: center;"> </td> 107 * <td style="text-align: center;">X</td> 108 * <td style="text-align: center;"> </td> 109 * <td style="text-align: center;"> </td> 110 * <td style="text-align: center;"> </td> 111 * <td style="text-align: center;">X</td> 112 * </tr> 113 * </table> 114 * <p> 115 * The {@link android.view.DragEvent#getAction()}, 116 * {@link android.view.DragEvent#getLocalState()} 117 * {@link android.view.DragEvent#describeContents()}, 118 * {@link android.view.DragEvent#writeToParcel(Parcel,int)}, and 119 * {@link android.view.DragEvent#toString()} methods always return valid data. 120 * </p> 121 * 122 * <div class="special reference"> 123 * <h3>Developer Guides</h3> 124 * <p>For a guide to implementing drag and drop features, read the 125 * <a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and Drop</a> developer guide.</p> 126 * </div> 127 */ 128 public class DragEvent implements Parcelable { 129 private static final boolean TRACK_RECYCLED_LOCATION = false; 130 131 int mAction; 132 float mX, mY; 133 @UnsupportedAppUsage 134 ClipDescription mClipDescription; 135 @UnsupportedAppUsage 136 ClipData mClipData; 137 IDragAndDropPermissions mDragAndDropPermissions; 138 139 Object mLocalState; 140 boolean mDragResult; 141 boolean mEventHandlerWasCalled; 142 143 private DragEvent mNext; 144 private RuntimeException mRecycledLocation; 145 private boolean mRecycled; 146 147 private static final int MAX_RECYCLED = 10; 148 private static final Object gRecyclerLock = new Object(); 149 private static int gRecyclerUsed = 0; 150 private static DragEvent gRecyclerTop = null; 151 152 /** 153 * Action constant returned by {@link #getAction()}: Signals the start of a 154 * drag and drop operation. The View should return {@code true} from its 155 * {@link View#onDragEvent(DragEvent) onDragEvent()} handler method or 156 * {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener 157 * if it can accept a drop. The onDragEvent() or onDrag() methods usually inspect the metadata 158 * from {@link #getClipDescription()} to determine if they can accept the data contained in 159 * this drag. For an operation that doesn't represent data transfer, these methods may 160 * perform other actions to determine whether or not the View should accept the data. 161 * If the View wants to indicate that it is a valid drop target, it can also react by 162 * changing its appearance. 163 * <p> 164 * Views added or becoming visible for the first time during a drag operation receive this 165 * event when they are added or becoming visible. 166 * </p> 167 * <p> 168 * A View only receives further drag events for the drag operation if it returns {@code true} 169 * in response to ACTION_DRAG_STARTED. 170 * </p> 171 * @see #ACTION_DRAG_ENDED 172 * @see #getX() 173 * @see #getY() 174 */ 175 public static final int ACTION_DRAG_STARTED = 1; 176 177 /** 178 * Action constant returned by {@link #getAction()}: Sent to a View after 179 * {@link #ACTION_DRAG_ENTERED} while the drag shadow is still within the View object's bounding 180 * box, but not within a descendant view that can accept the data. The {@link #getX()} and 181 * {@link #getY()} methods supply 182 * the X and Y position of of the drag point within the View object's bounding box. 183 * <p> 184 * A View receives an {@link #ACTION_DRAG_ENTERED} event before receiving any 185 * ACTION_DRAG_LOCATION events. 186 * </p> 187 * <p> 188 * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the 189 * drag shadow out of the View object's bounding box or into a descendant view that can accept 190 * the data. If the user moves the drag shadow back into the View object's bounding box or out 191 * of a descendant view that can accept the data, the View receives an ACTION_DRAG_ENTERED again 192 * before receiving any more ACTION_DRAG_LOCATION events. 193 * </p> 194 * @see #ACTION_DRAG_ENTERED 195 * @see #getX() 196 * @see #getY() 197 */ 198 public static final int ACTION_DRAG_LOCATION = 2; 199 200 /** 201 * Action constant returned by {@link #getAction()}: Signals to a View that the user 202 * has released the drag shadow, and the drag point is within the bounding box of the View and 203 * not within a descendant view that can accept the data. 204 * The View should retrieve the data from the DragEvent by calling {@link #getClipData()}. 205 * The methods {@link #getX()} and {@link #getY()} return the X and Y position of the drop point 206 * within the View object's bounding box. 207 * <p> 208 * The View should return {@code true} from its {@link View#onDragEvent(DragEvent)} 209 * handler or {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} 210 * listener if it accepted the drop, and {@code false} if it ignored the drop. 211 * </p> 212 * <p> 213 * The View can also react to this action by changing its appearance. 214 * </p> 215 * @see #getClipData() 216 * @see #getX() 217 * @see #getY() 218 */ 219 public static final int ACTION_DROP = 3; 220 221 /** 222 * Action constant returned by {@link #getAction()}: Signals to a View that the drag and drop 223 * operation has concluded. A View that changed its appearance during the operation should 224 * return to its usual drawing state in response to this event. 225 * <p> 226 * All views with listeners that returned boolean <code>true</code> for the ACTION_DRAG_STARTED 227 * event will receive the ACTION_DRAG_ENDED event even if they are not currently visible when 228 * the drag ends. Views removed during the drag operation won't receive the ACTION_DRAG_ENDED 229 * event. 230 * </p> 231 * <p> 232 * The View object can call {@link #getResult()} to see the result of the operation. 233 * If a View returned {@code true} in response to {@link #ACTION_DROP}, then 234 * getResult() returns {@code true}, otherwise it returns {@code false}. 235 * </p> 236 * @see #ACTION_DRAG_STARTED 237 * @see #getResult() 238 */ 239 public static final int ACTION_DRAG_ENDED = 4; 240 241 /** 242 * Action constant returned by {@link #getAction()}: Signals to a View that the drag point has 243 * entered the bounding box of the View. 244 * <p> 245 * If the View can accept a drop, it can react to ACTION_DRAG_ENTERED 246 * by changing its appearance in a way that tells the user that the View is the current 247 * drop target. 248 * </p> 249 * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the 250 * drag shadow out of the View object's bounding box or into a descendant view that can accept 251 * the data. If the user moves the drag shadow back into the View object's bounding box or out 252 * of a descendant view that can accept the data, the View receives an ACTION_DRAG_ENTERED again 253 * before receiving any more ACTION_DRAG_LOCATION events. 254 * </p> 255 * @see #ACTION_DRAG_ENTERED 256 * @see #ACTION_DRAG_LOCATION 257 */ 258 public static final int ACTION_DRAG_ENTERED = 5; 259 260 /** 261 * Action constant returned by {@link #getAction()}: Signals that the user has moved the 262 * drag shadow out of the bounding box of the View or into a descendant view that can accept 263 * the data. 264 * The View can react by changing its appearance in a way that tells the user that 265 * View is no longer the immediate drop target. 266 * <p> 267 * After the system sends an ACTION_DRAG_EXITED event to the View, the View receives no more 268 * ACTION_DRAG_LOCATION events until the user drags the drag shadow back over the View. 269 * </p> 270 * 271 */ 272 public static final int ACTION_DRAG_EXITED = 6; 273 DragEvent()274 private DragEvent() { 275 } 276 init(int action, float x, float y, ClipDescription description, ClipData data, IDragAndDropPermissions dragAndDropPermissions, Object localState, boolean result)277 private void init(int action, float x, float y, ClipDescription description, ClipData data, 278 IDragAndDropPermissions dragAndDropPermissions, Object localState, boolean result) { 279 mAction = action; 280 mX = x; 281 mY = y; 282 mClipDescription = description; 283 mClipData = data; 284 this.mDragAndDropPermissions = dragAndDropPermissions; 285 mLocalState = localState; 286 mDragResult = result; 287 } 288 obtain()289 static DragEvent obtain() { 290 return DragEvent.obtain(0, 0f, 0f, null, null, null, null, false); 291 } 292 293 /** @hide */ obtain(int action, float x, float y, Object localState, ClipDescription description, ClipData data, IDragAndDropPermissions dragAndDropPermissions, boolean result)294 public static DragEvent obtain(int action, float x, float y, Object localState, 295 ClipDescription description, ClipData data, 296 IDragAndDropPermissions dragAndDropPermissions, boolean result) { 297 final DragEvent ev; 298 synchronized (gRecyclerLock) { 299 if (gRecyclerTop == null) { 300 ev = new DragEvent(); 301 ev.init(action, x, y, description, data, dragAndDropPermissions, localState, 302 result); 303 return ev; 304 } 305 ev = gRecyclerTop; 306 gRecyclerTop = ev.mNext; 307 gRecyclerUsed -= 1; 308 } 309 ev.mRecycledLocation = null; 310 ev.mRecycled = false; 311 ev.mNext = null; 312 313 ev.init(action, x, y, description, data, dragAndDropPermissions, localState, result); 314 315 return ev; 316 } 317 318 /** @hide */ 319 @UnsupportedAppUsage obtain(DragEvent source)320 public static DragEvent obtain(DragEvent source) { 321 return obtain(source.mAction, source.mX, source.mY, source.mLocalState, 322 source.mClipDescription, source.mClipData, source.mDragAndDropPermissions, 323 source.mDragResult); 324 } 325 326 /** 327 * Inspect the action value of this event. 328 * @return One of the following action constants, in the order in which they usually occur 329 * during a drag and drop operation: 330 * <ul> 331 * <li>{@link #ACTION_DRAG_STARTED}</li> 332 * <li>{@link #ACTION_DRAG_ENTERED}</li> 333 * <li>{@link #ACTION_DRAG_LOCATION}</li> 334 * <li>{@link #ACTION_DROP}</li> 335 * <li>{@link #ACTION_DRAG_EXITED}</li> 336 * <li>{@link #ACTION_DRAG_ENDED}</li> 337 * </ul> 338 */ getAction()339 public int getAction() { 340 return mAction; 341 } 342 343 /** 344 * Gets the X coordinate of the drag point. The value is only valid if the event action is 345 * {@link #ACTION_DRAG_STARTED}, {@link #ACTION_DRAG_LOCATION} or {@link #ACTION_DROP}. 346 * @return The current drag point's X coordinate 347 */ getX()348 public float getX() { 349 return mX; 350 } 351 352 /** 353 * Gets the Y coordinate of the drag point. The value is only valid if the event action is 354 * {@link #ACTION_DRAG_STARTED}, {@link #ACTION_DRAG_LOCATION} or {@link #ACTION_DROP}. 355 * @return The current drag point's Y coordinate 356 */ getY()357 public float getY() { 358 return mY; 359 } 360 361 /** 362 * Returns the {@link android.content.ClipData} object sent to the system as part of the call 363 * to 364 * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int) 365 * startDragAndDrop()}. 366 * This method only returns valid data if the event action is {@link #ACTION_DROP}. 367 * @return The ClipData sent to the system by startDragAndDrop(). 368 */ getClipData()369 public ClipData getClipData() { 370 return mClipData; 371 } 372 373 /** 374 * Returns the {@link android.content.ClipDescription} object contained in the 375 * {@link android.content.ClipData} object sent to the system as part of the call to 376 * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int) 377 * startDragAndDrop()}. 378 * The drag handler or listener for a View can use the metadata in this object to decide if the 379 * View can accept the dragged View object's data. 380 * <p> 381 * This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}. 382 * @return The ClipDescription that was part of the ClipData sent to the system by 383 * startDragAndDrop(). 384 */ getClipDescription()385 public ClipDescription getClipDescription() { 386 return mClipDescription; 387 } 388 389 /** @hide */ getDragAndDropPermissions()390 public IDragAndDropPermissions getDragAndDropPermissions() { 391 return mDragAndDropPermissions; 392 } 393 394 /** 395 * Returns the local state object sent to the system as part of the call to 396 * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int) 397 * startDragAndDrop()}. 398 * The object is intended to provide local information about the drag and drop operation. For 399 * example, it can indicate whether the drag and drop operation is a copy or a move. 400 * <p> 401 * The local state is available only to views in the activity which has started the drag 402 * operation. In all other activities this method will return null 403 * </p> 404 * <p> 405 * This method returns valid data for all event actions. 406 * </p> 407 * @return The local state object sent to the system by startDragAndDrop(). 408 */ getLocalState()409 public Object getLocalState() { 410 return mLocalState; 411 } 412 413 /** 414 * <p> 415 * Returns an indication of the result of the drag and drop operation. 416 * This method only returns valid data if the action type is {@link #ACTION_DRAG_ENDED}. 417 * The return value depends on what happens after the user releases the drag shadow. 418 * </p> 419 * <p> 420 * If the user releases the drag shadow on a View that can accept a drop, the system sends an 421 * {@link #ACTION_DROP} event to the View object's drag event listener. If the listener 422 * returns {@code true}, then getResult() will return {@code true}. 423 * If the listener returns {@code false}, then getResult() returns {@code false}. 424 * </p> 425 * <p> 426 * Notice that getResult() also returns {@code false} if no {@link #ACTION_DROP} is sent. This 427 * happens, for example, when the user releases the drag shadow over an area outside of the 428 * application. In this case, the system sends out {@link #ACTION_DRAG_ENDED} for the current 429 * operation, but never sends out {@link #ACTION_DROP}. 430 * </p> 431 * @return {@code true} if a drag event listener returned {@code true} in response to 432 * {@link #ACTION_DROP}. If the system did not send {@link #ACTION_DROP} before 433 * {@link #ACTION_DRAG_ENDED}, or if the listener returned {@code false} in response to 434 * {@link #ACTION_DROP}, then {@code false} is returned. 435 */ getResult()436 public boolean getResult() { 437 return mDragResult; 438 } 439 440 /** 441 * Recycle the DragEvent, to be re-used by a later caller. After calling 442 * this function you must never touch the event again. 443 * 444 * @hide 445 */ recycle()446 public final void recycle() { 447 // Ensure recycle is only called once! 448 if (TRACK_RECYCLED_LOCATION) { 449 if (mRecycledLocation != null) { 450 throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation); 451 } 452 mRecycledLocation = new RuntimeException("Last recycled here"); 453 } else { 454 if (mRecycled) { 455 throw new RuntimeException(toString() + " recycled twice!"); 456 } 457 mRecycled = true; 458 } 459 460 mClipData = null; 461 mClipDescription = null; 462 mLocalState = null; 463 mEventHandlerWasCalled = false; 464 465 synchronized (gRecyclerLock) { 466 if (gRecyclerUsed < MAX_RECYCLED) { 467 gRecyclerUsed++; 468 mNext = gRecyclerTop; 469 gRecyclerTop = this; 470 } 471 } 472 } 473 474 /** 475 * Returns a string containing a concise, human-readable representation of this DragEvent 476 * object. 477 * @return A string representation of the DragEvent object. 478 */ 479 @Override toString()480 public String toString() { 481 return "DragEvent{" + Integer.toHexString(System.identityHashCode(this)) 482 + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription 483 + " data=" + mClipData + " local=" + mLocalState + " result=" + mDragResult 484 + "}"; 485 } 486 487 /* Parcelable interface */ 488 489 /** 490 * Returns information about the {@link android.os.Parcel} representation of this DragEvent 491 * object. 492 * @return Information about the {@link android.os.Parcel} representation. 493 */ describeContents()494 public int describeContents() { 495 return 0; 496 } 497 498 /** 499 * Creates a {@link android.os.Parcel} object from this DragEvent object. 500 * @param dest A {@link android.os.Parcel} object in which to put the DragEvent object. 501 * @param flags Flags to store in the Parcel. 502 */ writeToParcel(Parcel dest, int flags)503 public void writeToParcel(Parcel dest, int flags) { 504 dest.writeInt(mAction); 505 dest.writeFloat(mX); 506 dest.writeFloat(mY); 507 dest.writeInt(mDragResult ? 1 : 0); 508 if (mClipData == null) { 509 dest.writeInt(0); 510 } else { 511 dest.writeInt(1); 512 mClipData.writeToParcel(dest, flags); 513 } 514 if (mClipDescription == null) { 515 dest.writeInt(0); 516 } else { 517 dest.writeInt(1); 518 mClipDescription.writeToParcel(dest, flags); 519 } 520 if (mDragAndDropPermissions == null) { 521 dest.writeInt(0); 522 } else { 523 dest.writeInt(1); 524 dest.writeStrongBinder(mDragAndDropPermissions.asBinder()); 525 } 526 } 527 528 /** 529 * A container for creating a DragEvent from a Parcel. 530 */ 531 public static final @android.annotation.NonNull Parcelable.Creator<DragEvent> CREATOR = 532 new Parcelable.Creator<DragEvent>() { 533 public DragEvent createFromParcel(Parcel in) { 534 DragEvent event = DragEvent.obtain(); 535 event.mAction = in.readInt(); 536 event.mX = in.readFloat(); 537 event.mY = in.readFloat(); 538 event.mDragResult = (in.readInt() != 0); 539 if (in.readInt() != 0) { 540 event.mClipData = ClipData.CREATOR.createFromParcel(in); 541 } 542 if (in.readInt() != 0) { 543 event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in); 544 } 545 if (in.readInt() != 0) { 546 event.mDragAndDropPermissions = 547 IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());; 548 } 549 return event; 550 } 551 552 public DragEvent[] newArray(int size) { 553 return new DragEvent[size]; 554 } 555 }; 556 } 557