diff options
| author | Po Lu | 2023-10-14 10:15:20 +0800 |
|---|---|---|
| committer | Po Lu | 2023-10-14 10:15:42 +0800 |
| commit | 03f5a06a052ee0b4b8b77b4460ead717b87c4798 (patch) | |
| tree | c047e2c53e56e6c6e20b5475a6e33fd1b25ece96 /java | |
| parent | 0dd7e6e3aeac1ab0a03f2ed2ad108deecde82447 (diff) | |
| download | emacs-03f5a06a052ee0b4b8b77b4460ead717b87c4798.tar.gz emacs-03f5a06a052ee0b4b8b77b4460ead717b87c4798.zip | |
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.
Diffstat (limited to 'java')
| -rw-r--r-- | java/org/gnu/emacs/EmacsNative.java | 11 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsView.java | 14 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsWindow.java | 138 |
3 files changed, 160 insertions, 3 deletions
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index d8524d92130..7d7e1e5d831 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java | |||
| @@ -175,6 +175,17 @@ public final class EmacsNative | |||
| 175 | public static native long sendExpose (short window, int x, int y, | 175 | public static native long sendExpose (short window, int x, int y, |
| 176 | int width, int height); | 176 | int width, int height); |
| 177 | 177 | ||
| 178 | /* Send an ANDROID_DND_DRAG event. */ | ||
| 179 | public static native long sendDndDrag (short window, int x, int y); | ||
| 180 | |||
| 181 | /* Send an ANDROID_DND_URI event. */ | ||
| 182 | public static native long sendDndUri (short window, int x, int y, | ||
| 183 | String text); | ||
| 184 | |||
| 185 | /* Send an ANDROID_DND_TEXT event. */ | ||
| 186 | public static native long sendDndText (short window, int x, int y, | ||
| 187 | String text); | ||
| 188 | |||
| 178 | /* Return the file name associated with the specified file | 189 | /* Return the file name associated with the specified file |
| 179 | descriptor, or NULL if there is none. */ | 190 | descriptor, or NULL if there is none. */ |
| 180 | public static native byte[] getProcName (int fd); | 191 | public static native byte[] getProcName (int fd); |
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 877b1ce2429..2d53231fbf9 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java | |||
| @@ -24,6 +24,7 @@ import android.content.Context; | |||
| 24 | import android.text.InputType; | 24 | import android.text.InputType; |
| 25 | 25 | ||
| 26 | import android.view.ContextMenu; | 26 | import android.view.ContextMenu; |
| 27 | import android.view.DragEvent; | ||
| 27 | import android.view.View; | 28 | import android.view.View; |
| 28 | import android.view.KeyEvent; | 29 | import android.view.KeyEvent; |
| 29 | import android.view.MotionEvent; | 30 | import android.view.MotionEvent; |
| @@ -566,6 +567,19 @@ public final class EmacsView extends ViewGroup | |||
| 566 | return window.onTouchEvent (motion); | 567 | return window.onTouchEvent (motion); |
| 567 | } | 568 | } |
| 568 | 569 | ||
| 570 | @Override | ||
| 571 | public boolean | ||
| 572 | onDragEvent (DragEvent drag) | ||
| 573 | { | ||
| 574 | /* Inter-program drag and drop isn't supported under Android 23 | ||
| 575 | and earlier. */ | ||
| 576 | |||
| 577 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) | ||
| 578 | return false; | ||
| 579 | |||
| 580 | return window.onDragEvent (drag); | ||
| 581 | } | ||
| 582 | |||
| 569 | 583 | ||
| 570 | 584 | ||
| 571 | private void | 585 | private void |
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 8d444aa27f5..3d2d86624a7 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java | |||
| @@ -27,6 +27,8 @@ import java.util.HashMap; | |||
| 27 | import java.util.LinkedHashMap; | 27 | import java.util.LinkedHashMap; |
| 28 | import java.util.Map; | 28 | import java.util.Map; |
| 29 | 29 | ||
| 30 | import android.content.ClipData; | ||
| 31 | import android.content.ClipDescription; | ||
| 30 | import android.content.Context; | 32 | import android.content.Context; |
| 31 | 33 | ||
| 32 | import android.graphics.Rect; | 34 | import android.graphics.Rect; |
| @@ -34,12 +36,15 @@ import android.graphics.Canvas; | |||
| 34 | import android.graphics.Bitmap; | 36 | import android.graphics.Bitmap; |
| 35 | import android.graphics.PixelFormat; | 37 | import android.graphics.PixelFormat; |
| 36 | 38 | ||
| 37 | import android.view.View; | 39 | import android.net.Uri; |
| 38 | import android.view.ViewManager; | 40 | |
| 41 | import android.view.DragEvent; | ||
| 39 | import android.view.Gravity; | 42 | import android.view.Gravity; |
| 43 | import android.view.InputDevice; | ||
| 40 | import android.view.KeyEvent; | 44 | import android.view.KeyEvent; |
| 41 | import android.view.MotionEvent; | 45 | import android.view.MotionEvent; |
| 42 | import android.view.InputDevice; | 46 | import android.view.View; |
| 47 | import android.view.ViewManager; | ||
| 43 | import android.view.WindowManager; | 48 | import android.view.WindowManager; |
| 44 | 49 | ||
| 45 | import android.util.Log; | 50 | import android.util.Log; |
| @@ -1560,4 +1565,131 @@ public final class EmacsWindow extends EmacsHandleObject | |||
| 1560 | rect.width (), rect.height ()); | 1565 | rect.width (), rect.height ()); |
| 1561 | } | 1566 | } |
| 1562 | } | 1567 | } |
| 1568 | |||
| 1569 | |||
| 1570 | |||
| 1571 | /* Drag and drop. | ||
| 1572 | |||
| 1573 | Android 7.0 and later permit multiple windows to be juxtaposed | ||
| 1574 | on-screen, consequently enabling items selected from one window | ||
| 1575 | to be dragged onto another. Data is transferred across program | ||
| 1576 | boundaries using ClipData items, much the same way clipboard data | ||
| 1577 | is transferred. | ||
| 1578 | |||
| 1579 | When an item is dropped, Emacs must ascertain whether the clip | ||
| 1580 | data represents plain text, a content URI incorporating a file, | ||
| 1581 | or some other data. This is implemented by examining the clip | ||
| 1582 | data's ``description'', which enumerates each of the MIME data | ||
| 1583 | types the clip data is capable of providing data in. | ||
| 1584 | |||
| 1585 | If the clip data represents plain text, then that text is copied | ||
| 1586 | into a string and conveyed to Lisp code. Otherwise, Emacs must | ||
| 1587 | solicit rights to access the URI from the system, absent which it | ||
| 1588 | is accounted plain text and reinterpreted as such, to cue the | ||
| 1589 | user that something has gone awry. | ||
| 1590 | |||
| 1591 | Moreover, events are regularly sent as the item being dragged | ||
| 1592 | travels across the frame, even if it might not be dropped. This | ||
| 1593 | facilitates cursor motion and scrolling in response, as provided | ||
| 1594 | by the options dnd-indicate-insertion-point and | ||
| 1595 | dnd-scroll-margin. */ | ||
| 1596 | |||
| 1597 | /* Register the drag and drop event EVENT. */ | ||
| 1598 | |||
| 1599 | public boolean | ||
| 1600 | onDragEvent (DragEvent event) | ||
| 1601 | { | ||
| 1602 | ClipData data; | ||
| 1603 | ClipDescription description; | ||
| 1604 | int i, x, y; | ||
| 1605 | String type; | ||
| 1606 | Uri uri; | ||
| 1607 | EmacsActivity activity; | ||
| 1608 | |||
| 1609 | x = (int) event.getX (); | ||
| 1610 | y = (int) event.getY (); | ||
| 1611 | |||
| 1612 | switch (event.getAction ()) | ||
| 1613 | { | ||
| 1614 | case DragEvent.ACTION_DRAG_STARTED: | ||
| 1615 | /* Return true to continue the drag and drop operation. */ | ||
| 1616 | return true; | ||
| 1617 | |||
| 1618 | case DragEvent.ACTION_DRAG_LOCATION: | ||
| 1619 | /* Send this drag motion event to Emacs. */ | ||
| 1620 | EmacsNative.sendDndDrag (handle, x, y); | ||
| 1621 | return true; | ||
| 1622 | |||
| 1623 | case DragEvent.ACTION_DROP: | ||
| 1624 | /* Judge whether this is plain text, or if it's a file URI for | ||
| 1625 | which permissions must be requested. */ | ||
| 1626 | |||
| 1627 | data = event.getClipData (); | ||
| 1628 | description = data.getDescription (); | ||
| 1629 | |||
| 1630 | /* If there are insufficient items within the clip data, | ||
| 1631 | return false. */ | ||
| 1632 | |||
| 1633 | if (data.getItemCount () < 1) | ||
| 1634 | return false; | ||
| 1635 | |||
| 1636 | /* Search for plain text data within the clipboard. */ | ||
| 1637 | |||
| 1638 | for (i = 0; i < description.getMimeTypeCount (); ++i) | ||
| 1639 | { | ||
| 1640 | type = description.getMimeType (i); | ||
| 1641 | |||
| 1642 | if (type.equals (ClipDescription.MIMETYPE_TEXT_PLAIN) | ||
| 1643 | || type.equals (ClipDescription.MIMETYPE_TEXT_HTML)) | ||
| 1644 | { | ||
| 1645 | /* The data being dropped is plain text; encode it | ||
| 1646 | suitably and send it to the main thread. */ | ||
| 1647 | type = (data.getItemAt (0).coerceToText (EmacsService.SERVICE) | ||
| 1648 | .toString ()); | ||
| 1649 | EmacsNative.sendDndText (handle, x, y, type); | ||
| 1650 | return true; | ||
| 1651 | } | ||
| 1652 | else if (type.equals (ClipDescription.MIMETYPE_TEXT_URILIST)) | ||
| 1653 | { | ||
| 1654 | /* The data being dropped is a list of URIs; encode it | ||
| 1655 | suitably and send it to the main thread. */ | ||
| 1656 | type = (data.getItemAt (0).coerceToText (EmacsService.SERVICE) | ||
| 1657 | .toString ()); | ||
| 1658 | EmacsNative.sendDndUri (handle, x, y, type); | ||
| 1659 | return true; | ||
| 1660 | } | ||
| 1661 | else | ||
| 1662 | { | ||
| 1663 | /* If the item dropped is a URI, send it to the main | ||
| 1664 | thread. */ | ||
| 1665 | uri = data.getItemAt (0).getUri (); | ||
| 1666 | |||
| 1667 | /* Attempt to acquire permissions for this URI; | ||
| 1668 | failing which, insert it as text instead. */ | ||
| 1669 | |||
| 1670 | if (uri.getScheme () != null | ||
| 1671 | && uri.getScheme ().equals ("content") | ||
| 1672 | && (activity = EmacsActivity.lastFocusedActivity) != null) | ||
| 1673 | { | ||
| 1674 | if (activity.requestDragAndDropPermissions (event) == null) | ||
| 1675 | uri = null; | ||
| 1676 | } | ||
| 1677 | |||
| 1678 | if (uri != null) | ||
| 1679 | EmacsNative.sendDndUri (handle, x, y, uri.toString ()); | ||
| 1680 | else | ||
| 1681 | { | ||
| 1682 | type = (data.getItemAt (0) | ||
| 1683 | .coerceToText (EmacsService.SERVICE) | ||
| 1684 | .toString ()); | ||
| 1685 | EmacsNative.sendDndText (handle, x, y, type); | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | return true; | ||
| 1689 | } | ||
| 1690 | } | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | return true; | ||
| 1694 | } | ||
| 1563 | }; | 1695 | }; |