1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-14 01:50:22 -08:00

Implement multi-window drag-and-drop under Android

* java/org/gnu/emacs/EmacsNative.java (sendDndDrag, sendDndUri)
(sendDndText): Declare new event-sending functions.

* java/org/gnu/emacs/EmacsView.java (onDragEvent): New function.

* java/org/gnu/emacs/EmacsWindow.java (onDragEvent): New
function; respond to each drag and drop event, request
permissions if necessary and transfer dropped data to Lisp.

* lisp/dnd.el (dnd-unescape-file-uris): New variable.
(dnd-get-local-file-name): If that variable is nil, refrain from
unescaping URLs provided.

* lisp/term/android-win.el (android-handle-dnd-event): New
function.
(special-event-map): Bind drag-n-drop-event.

* src/android.c (sendDndDrag, sendDndUri, sendDndText): New
functions.

* src/androidgui.h (enum android_event_type): New event types
ANDROID_DND_DRAG_EVENT, ANDROID_DND_URI_EVENT,
ANDROID_DND_TEXT_EVENT.
(struct android_dnd_event): New structure.
(union android_event) <dnd>: New field.

* src/androidterm.c (handle_one_android_event)
<ANDROID_DND_..._EVENT>: Generate drag-n-drop events for each
of these types.
(syms_of_androidterm) <Quri, Qtext>: New defsyms.
This commit is contained in:
Po Lu 2023-10-14 10:15:20 +08:00
parent 0dd7e6e3ae
commit 03f5a06a05
8 changed files with 394 additions and 4 deletions

View file

@ -27,6 +27,8 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.graphics.Rect;
@ -34,12 +36,15 @@ import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.view.View;
import android.view.ViewManager;
import android.net.Uri;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.InputDevice;
import android.view.View;
import android.view.ViewManager;
import android.view.WindowManager;
import android.util.Log;
@ -1560,4 +1565,131 @@ public final class EmacsWindow extends EmacsHandleObject
rect.width (), rect.height ());
}
}
/* Drag and drop.
Android 7.0 and later permit multiple windows to be juxtaposed
on-screen, consequently enabling items selected from one window
to be dragged onto another. Data is transferred across program
boundaries using ClipData items, much the same way clipboard data
is transferred.
When an item is dropped, Emacs must ascertain whether the clip
data represents plain text, a content URI incorporating a file,
or some other data. This is implemented by examining the clip
data's ``description'', which enumerates each of the MIME data
types the clip data is capable of providing data in.
If the clip data represents plain text, then that text is copied
into a string and conveyed to Lisp code. Otherwise, Emacs must
solicit rights to access the URI from the system, absent which it
is accounted plain text and reinterpreted as such, to cue the
user that something has gone awry.
Moreover, events are regularly sent as the item being dragged
travels across the frame, even if it might not be dropped. This
facilitates cursor motion and scrolling in response, as provided
by the options dnd-indicate-insertion-point and
dnd-scroll-margin. */
/* Register the drag and drop event EVENT. */
public boolean
onDragEvent (DragEvent event)
{
ClipData data;
ClipDescription description;
int i, x, y;
String type;
Uri uri;
EmacsActivity activity;
x = (int) event.getX ();
y = (int) event.getY ();
switch (event.getAction ())
{
case DragEvent.ACTION_DRAG_STARTED:
/* Return true to continue the drag and drop operation. */
return true;
case DragEvent.ACTION_DRAG_LOCATION:
/* Send this drag motion event to Emacs. */
EmacsNative.sendDndDrag (handle, x, y);
return true;
case DragEvent.ACTION_DROP:
/* Judge whether this is plain text, or if it's a file URI for
which permissions must be requested. */
data = event.getClipData ();
description = data.getDescription ();
/* If there are insufficient items within the clip data,
return false. */
if (data.getItemCount () < 1)
return false;
/* Search for plain text data within the clipboard. */
for (i = 0; i < description.getMimeTypeCount (); ++i)
{
type = description.getMimeType (i);
if (type.equals (ClipDescription.MIMETYPE_TEXT_PLAIN)
|| type.equals (ClipDescription.MIMETYPE_TEXT_HTML))
{
/* The data being dropped is plain text; encode it
suitably and send it to the main thread. */
type = (data.getItemAt (0).coerceToText (EmacsService.SERVICE)
.toString ());
EmacsNative.sendDndText (handle, x, y, type);
return true;
}
else if (type.equals (ClipDescription.MIMETYPE_TEXT_URILIST))
{
/* The data being dropped is a list of URIs; encode it
suitably and send it to the main thread. */
type = (data.getItemAt (0).coerceToText (EmacsService.SERVICE)
.toString ());
EmacsNative.sendDndUri (handle, x, y, type);
return true;
}
else
{
/* If the item dropped is a URI, send it to the main
thread. */
uri = data.getItemAt (0).getUri ();
/* Attempt to acquire permissions for this URI;
failing which, insert it as text instead. */
if (uri.getScheme () != null
&& uri.getScheme ().equals ("content")
&& (activity = EmacsActivity.lastFocusedActivity) != null)
{
if (activity.requestDragAndDropPermissions (event) == null)
uri = null;
}
if (uri != null)
EmacsNative.sendDndUri (handle, x, y, uri.toString ());
else
{
type = (data.getItemAt (0)
.coerceToText (EmacsService.SERVICE)
.toString ());
EmacsNative.sendDndText (handle, x, y, type);
}
return true;
}
}
}
return true;
}
};