From 4b7b90e79182805dd52fb0b779a0b4ab08983ab0 Mon Sep 17 00:00:00 2001
From: Nikita Dubrovsky
Date: Fri, 12 Mar 2021 09:41:53 -0800
Subject: [PATCH] Integrate OnReceiveContentListener in View.onDragEvent
OnReceiveContentListener was already integrated with drag-and-drop
in TextView. The change extends support to all subclasses of View.
If an OnReceiveContentListener is set on the view, the default
implementation of View.onDragEvent will now return true for an
ACTION_DRAG_STARTED event and will call
View.performReceiveContent for an ACTION_DROP event.
Bug: 182617122
Test: atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests
Test: atest CtsViewTestCases:ViewOnReceiveContentTest
Test: atest CtsWidgetTestCases:TextViewOnReceiveContentTest
Test: Manually using ReceiveContentDemo
Change-Id: I98fb703220ebd1953b20dcab60d22847cc5000d8
---
core/java/android/view/View.java | 34 ++++++++++++++++++++++++++
core/java/android/widget/TextView.java | 28 ++++++++++++++++++++-
2 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 35d5d8ec6c3dd..7455b8bc3cf32 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,7 @@
package android.view;
import static android.content.res.Resources.ID_NULL;
+import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -26747,6 +26748,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link android.view.DragEvent#getAction()} returns one of the action type constants defined
* in DragEvent. The method uses these to determine what is happening in the drag and drop
* operation.
+ *
+ *
+ * The default implementation returns false, except if an {@link OnReceiveContentListener}
+ * is {@link #setOnReceiveContentListener set} for this view. If an
+ * {@link OnReceiveContentListener} is set, the default implementation...
+ *
+ * - returns true for an
+ * {@link android.view.DragEvent#ACTION_DRAG_STARTED ACTION_DRAG_STARTED} event
+ *
- calls {@link #performReceiveContent} for an
+ * {@link android.view.DragEvent#ACTION_DROP ACTION_DROP} event
+ *
- returns true for an {@link android.view.DragEvent#ACTION_DROP ACTION_DROP} event, if
+ * the listener consumed some or all of the content
+ *
+ *
+ *
* @param event The {@link android.view.DragEvent} sent by the system.
* The {@link android.view.DragEvent#getAction()} method returns an action type constant defined
* in DragEvent, indicating the type of drag event represented by this object.
@@ -26766,6 +26782,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
*/
public boolean onDragEvent(DragEvent event) {
+ if (mListenerInfo == null || mListenerInfo.mOnReceiveContentListener == null) {
+ return false;
+ }
+ // Accept drag events by default if there's an OnReceiveContentListener set.
+ if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+ return true;
+ }
+ if (event.getAction() == DragEvent.ACTION_DROP) {
+ final DragAndDropPermissions permissions = DragAndDropPermissions.obtain(event);
+ if (permissions != null) {
+ permissions.takeTransient();
+ }
+ final ContentInfo payload = new ContentInfo.Builder(
+ event.getClipData(), SOURCE_DRAG_AND_DROP).build();
+ ContentInfo remainingPayload = performReceiveContent(payload);
+ // Return true unless none of the payload was consumed.
+ return remainingPayload != payload;
+ }
return false;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fdc66fcb81d89..71b5b3d676490 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -13059,11 +13059,37 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return getLayout().getOffsetForHorizontal(line, x);
}
+ /**
+ * Handles drag events sent by the system following a call to
+ * {@link android.view.View#startDragAndDrop(ClipData,DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
+ *
+ * If this text view is not editable, delegates to the default {@link View#onDragEvent}
+ * implementation.
+ *
+ *
If this text view is editable, accepts all drag actions (returns true for an
+ * {@link android.view.DragEvent#ACTION_DRAG_STARTED ACTION_DRAG_STARTED} event and all
+ * subsequent drag events). While the drag is in progress, updates the cursor position
+ * to follow the touch location. Once a drop event is received, handles content insertion
+ * via {@link #performReceiveContent}.
+ *
+ * @param event The {@link android.view.DragEvent} sent by the system.
+ * The {@link android.view.DragEvent#getAction()} method returns an action type constant
+ * defined in DragEvent, indicating the type of drag event represented by this object.
+ * @return Returns true if this text view is editable and delegates to super otherwise.
+ * See {@link View#onDragEvent}.
+ */
@Override
public boolean onDragEvent(DragEvent event) {
+ if (mEditor == null || !mEditor.hasInsertionController()) {
+ // If this TextView is not editable, defer to the default View implementation. This
+ // will check for the presence of an OnReceiveContentListener and accept/reject
+ // drag events depending on whether the listener is/isn't set.
+ return super.onDragEvent(event);
+ }
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
- return mEditor != null && mEditor.hasInsertionController();
+ return true;
case DragEvent.ACTION_DRAG_ENTERED:
TextView.this.requestFocus();