diff options
| author | Po Lu | 2023-01-28 16:29:22 +0800 |
|---|---|---|
| committer | Po Lu | 2023-01-28 16:29:22 +0800 |
| commit | 198b8160cfeeb178d3b2073c8d03afdafe338908 (patch) | |
| tree | 590c73006536ddfcc8acd8eff8fb80e31c2a009b | |
| parent | 5bd38905ac67221503f027737912fa0df3602a02 (diff) | |
| download | emacs-198b8160cfeeb178d3b2073c8d03afdafe338908.tar.gz emacs-198b8160cfeeb178d3b2073c8d03afdafe338908.zip | |
Update Android port
* doc/emacs/android.texi (Android File System): Describe an
easier way to disable scoped storage.
* java/AndroidManifest.xml.in: Add new permission to allow that.
* java/README: Add more text describing Java.
* java/org/gnu/emacs/EmacsContextMenu.java (Item): New fields
`isCheckable' and `isChecked'.
(EmacsContextMenu, addItem): New arguments.
(inflateMenuItems): Set checked status as appropriate.
* java/org/gnu/emacs/EmacsCopyArea.java (perform): Disallow
operations where width and height are less than or equal to
zero.
* lisp/menu-bar.el (menu-bar-edit-menu): Make
execute-extended-command available as a menu item.
* src/androidmenu.c (android_init_emacs_context_menu)
(android_menu_show):
* src/menu.c (have_boxes): Implement menu check boxes.
| -rw-r--r-- | doc/emacs/android.texi | 20 | ||||
| -rw-r--r-- | java/AndroidManifest.xml.in | 4 | ||||
| -rw-r--r-- | java/README | 63 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsContextMenu.java | 22 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsCopyArea.java | 6 | ||||
| -rw-r--r-- | lisp/menu-bar.el | 5 | ||||
| -rw-r--r-- | src/androidmenu.c | 17 | ||||
| -rw-r--r-- | src/menu.c | 2 |
8 files changed, 123 insertions, 16 deletions
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 98d7f1e1d9e..ae4080994b8 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi | |||
| @@ -177,17 +177,27 @@ makes the system more secure. Unfortunately, it also means that Emacs | |||
| 177 | cannot access files in those directories, despite holding the | 177 | cannot access files in those directories, despite holding the |
| 178 | necessary permissions. Thankfully, the Open Handset Alliance's | 178 | necessary permissions. Thankfully, the Open Handset Alliance's |
| 179 | version of Android allows this restriction to be disabled on a | 179 | version of Android allows this restriction to be disabled on a |
| 180 | per-program basis; the corresponding option in the system settings | 180 | per-program basis; on Android 10, the corresponding option in the |
| 181 | panel is: | 181 | system settings panel is: |
| 182 | 182 | ||
| 183 | @indentedblock | 183 | @indentedblock |
| 184 | System -> Developer Options -> App Compatibility Changes -> Emacs -> | 184 | System -> Developer Options -> App Compatibility Changes -> Emacs -> |
| 185 | DEFAULT_SCOPED_STORAGE | 185 | DEFAULT_SCOPED_STORAGE |
| 186 | @end indentedblock | 186 | @end indentedblock |
| 187 | 187 | ||
| 188 | After you disable this setting and grant Emacs the ``Files and | 188 | And on Android 11 and later, the corresponding option in the systems |
| 189 | Media'' permission, it will be able to access files under | 189 | settings panel is: |
| 190 | @file{/sdcard} as usual. | 190 | |
| 191 | @indentedblock | ||
| 192 | System -> Apps -> Special App Access -> All files access -> Emacs | ||
| 193 | @end indentedblock | ||
| 194 | |||
| 195 | After you disable or enable this setting as appropriate and grant | ||
| 196 | Emacs the ``Files and Media'' permission, it will be able to access | ||
| 197 | files under @file{/sdcard} as usual. | ||
| 198 | |||
| 199 | These settings are not present on many proprietary versions of | ||
| 200 | Android. | ||
| 191 | 201 | ||
| 192 | @node Android Environment | 202 | @node Android Environment |
| 193 | @section Running Emacs under Android | 203 | @section Running Emacs under Android |
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index 527ce74c474..09e4e788e0b 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in | |||
| @@ -52,6 +52,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. --> | |||
| 52 | <uses-permission android:name="android.permission.RECORD_AUDIO" /> | 52 | <uses-permission android:name="android.permission.RECORD_AUDIO" /> |
| 53 | <uses-permission android:name="android.permission.CAMERA" /> | 53 | <uses-permission android:name="android.permission.CAMERA" /> |
| 54 | 54 | ||
| 55 | <!-- This is required on Android 11 or later to access /sdcard. --> | ||
| 56 | |||
| 57 | <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> | ||
| 58 | |||
| 55 | <uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@" | 59 | <uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@" |
| 56 | android:targetSdkVersion="33"/> | 60 | android:targetSdkVersion="33"/> |
| 57 | 61 | ||
diff --git a/java/README b/java/README index 44f5a415162..3bce2556403 100644 --- a/java/README +++ b/java/README | |||
| @@ -292,15 +292,15 @@ public class EmacsFrobinicator | |||
| 292 | } | 292 | } |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | Java arrays are similar to C arrays in that they can not grow. But | 295 | Java arrays are similar to C arrays in that they can not grow. But |
| 296 | they are very much unlike C arrays in that they are always references | 296 | they are very much unlike C arrays in that they are always references |
| 297 | (as opposed to decaying into pointers in various situations), and | 297 | (as opposed to decaying into pointers in only some situations), and |
| 298 | contain information about their length. | 298 | contain information about their length. |
| 299 | 299 | ||
| 300 | If another function named ``frobinicate1'' takes an array as an | 300 | If another function named ``frobinicate1'' takes an array as an |
| 301 | argument, then it need not take the length of the array. | 301 | argument, then it need not take the length of the array. |
| 302 | 302 | ||
| 303 | Instead, it simply iterates over the array like so: | 303 | Instead, it may simply iterate over the array like so: |
| 304 | 304 | ||
| 305 | int i, k; | 305 | int i, k; |
| 306 | 306 | ||
| @@ -339,10 +339,65 @@ struct emacs_array_container | |||
| 339 | 339 | ||
| 340 | or, possibly even better, | 340 | or, possibly even better, |
| 341 | 341 | ||
| 342 | typedef int my_array[10]; | 342 | typedef int emacs_array_container[10]; |
| 343 | 343 | ||
| 344 | Alas, Java has no equivalent of `typedef'. | 344 | Alas, Java has no equivalent of `typedef'. |
| 345 | 345 | ||
| 346 | Like in C, Java string literals are delimited by double quotes. | ||
| 347 | Unlike C, however, strings are not NULL-terminated arrays of | ||
| 348 | characters, but a distinct type named ``String''. They store their | ||
| 349 | own length, characters in Java's 16-bit ``char'' type, and are capable | ||
| 350 | of holding NULL bytes. | ||
| 351 | |||
| 352 | Instead of writing: | ||
| 353 | |||
| 354 | wchar_t character; | ||
| 355 | extern char *s; | ||
| 356 | size_t s; | ||
| 357 | |||
| 358 | for (/* determine n, s in a loop. */) | ||
| 359 | s += mbstowc (&character, s, n); | ||
| 360 | |||
| 361 | or: | ||
| 362 | |||
| 363 | const char *byte; | ||
| 364 | |||
| 365 | for (byte = my_string; *byte; ++byte) | ||
| 366 | /* do something with *byte. */; | ||
| 367 | |||
| 368 | or perhaps even: | ||
| 369 | |||
| 370 | size_t length, i; | ||
| 371 | char foo; | ||
| 372 | |||
| 373 | length = strlen (my_string); | ||
| 374 | |||
| 375 | for (i = 0; i < length; ++i) | ||
| 376 | foo = my_string[i]; | ||
| 377 | |||
| 378 | you write: | ||
| 379 | |||
| 380 | char foo; | ||
| 381 | int i; | ||
| 382 | |||
| 383 | for (i = 0; i < myString.length (); ++i) | ||
| 384 | foo = myString.charAt (0); | ||
| 385 | |||
| 386 | Java also has stricter rules on what can be used as a truth value in a | ||
| 387 | conditional. While in C, any non-zero value is true, Java requires | ||
| 388 | that every truth value be of the boolean type ``boolean''. | ||
| 389 | |||
| 390 | What this means is that instead of simply writing: | ||
| 391 | |||
| 392 | if (foo || bar) | ||
| 393 | |||
| 394 | where foo can either be 1 or 0, and bar can either be NULL or a | ||
| 395 | pointer to something, you must explicitly write: | ||
| 396 | |||
| 397 | if (foo != 0 || bar != null) | ||
| 398 | |||
| 399 | in Java. | ||
| 400 | |||
| 346 | JAVA NATIVE INTERFACE | 401 | JAVA NATIVE INTERFACE |
| 347 | 402 | ||
| 348 | Java also provides an interface for C code to interface with Java. | 403 | Java also provides an interface for C code to interface with Java. |
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 056d8fb692c..92429410d03 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java | |||
| @@ -56,7 +56,7 @@ public class EmacsContextMenu | |||
| 56 | public int itemID; | 56 | public int itemID; |
| 57 | public String itemName; | 57 | public String itemName; |
| 58 | public EmacsContextMenu subMenu; | 58 | public EmacsContextMenu subMenu; |
| 59 | public boolean isEnabled; | 59 | public boolean isEnabled, isCheckable, isChecked; |
| 60 | 60 | ||
| 61 | @Override | 61 | @Override |
| 62 | public boolean | 62 | public boolean |
| @@ -108,10 +108,15 @@ public class EmacsContextMenu | |||
| 108 | 108 | ||
| 109 | /* Add a normal menu item to the context menu with the id ITEMID and | 109 | /* Add a normal menu item to the context menu with the id ITEMID and |
| 110 | the name ITEMNAME. Enable it if ISENABLED, else keep it | 110 | the name ITEMNAME. Enable it if ISENABLED, else keep it |
| 111 | disabled. */ | 111 | disabled. |
| 112 | |||
| 113 | If this is not a submenu and ISCHECKABLE is set, make the item | ||
| 114 | checkable. Likewise, if ISCHECKED is set, make the item | ||
| 115 | checked. */ | ||
| 112 | 116 | ||
| 113 | public void | 117 | public void |
| 114 | addItem (int itemID, String itemName, boolean isEnabled) | 118 | addItem (int itemID, String itemName, boolean isEnabled, |
| 119 | boolean isCheckable, boolean isChecked) | ||
| 115 | { | 120 | { |
| 116 | Item item; | 121 | Item item; |
| 117 | 122 | ||
| @@ -119,6 +124,8 @@ public class EmacsContextMenu | |||
| 119 | item.itemID = itemID; | 124 | item.itemID = itemID; |
| 120 | item.itemName = itemName; | 125 | item.itemName = itemName; |
| 121 | item.isEnabled = isEnabled; | 126 | item.isEnabled = isEnabled; |
| 127 | item.isCheckable = isCheckable; | ||
| 128 | item.isChecked = isChecked; | ||
| 122 | 129 | ||
| 123 | menuItems.add (item); | 130 | menuItems.add (item); |
| 124 | } | 131 | } |
| @@ -198,6 +205,15 @@ public class EmacsContextMenu | |||
| 198 | /* If the item ID is zero, then disable the item. */ | 205 | /* If the item ID is zero, then disable the item. */ |
| 199 | if (item.itemID == 0 || !item.isEnabled) | 206 | if (item.itemID == 0 || !item.isEnabled) |
| 200 | menuItem.setEnabled (false); | 207 | menuItem.setEnabled (false); |
| 208 | |||
| 209 | /* Now make the menu item display a checkmark as | ||
| 210 | appropriate. */ | ||
| 211 | |||
| 212 | if (item.isCheckable) | ||
| 213 | menuItem.setCheckable (true); | ||
| 214 | |||
| 215 | if (item.isChecked) | ||
| 216 | menuItem.setChecked (true); | ||
| 201 | } | 217 | } |
| 202 | } | 218 | } |
| 203 | } | 219 | } |
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java index 7a97d706794..f8974e17c2e 100644 --- a/java/org/gnu/emacs/EmacsCopyArea.java +++ b/java/org/gnu/emacs/EmacsCopyArea.java | |||
| @@ -99,6 +99,12 @@ public class EmacsCopyArea | |||
| 99 | if (src_y + height > srcBitmap.getHeight ()) | 99 | if (src_y + height > srcBitmap.getHeight ()) |
| 100 | height = srcBitmap.getHeight () - src_y; | 100 | height = srcBitmap.getHeight () - src_y; |
| 101 | 101 | ||
| 102 | /* If width and height are empty or negative, then skip the entire | ||
| 103 | CopyArea operation lest createBitmap throw an exception. */ | ||
| 104 | |||
| 105 | if (width <= 0 || height <= 0) | ||
| 106 | return; | ||
| 107 | |||
| 102 | rect = new Rect (dest_x, dest_y, dest_x + width, | 108 | rect = new Rect (dest_x, dest_y, dest_x + width, |
| 103 | dest_y + height); | 109 | dest_y + height); |
| 104 | 110 | ||
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index d020cf6e90a..2d907fb3827 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el | |||
| @@ -472,6 +472,11 @@ | |||
| 472 | (defvar menu-bar-edit-menu | 472 | (defvar menu-bar-edit-menu |
| 473 | (let ((menu (make-sparse-keymap "Edit"))) | 473 | (let ((menu (make-sparse-keymap "Edit"))) |
| 474 | 474 | ||
| 475 | (bindings--define-key menu [execute-extended-command] | ||
| 476 | '(menu-item "Execute Command" execute-extended-command | ||
| 477 | :enable t | ||
| 478 | :help "Read a command name, its arguments, then call it.")) | ||
| 479 | |||
| 475 | ;; ns-win.el said: Add spell for platform consistency. | 480 | ;; ns-win.el said: Add spell for platform consistency. |
| 476 | (if (featurep 'ns) | 481 | (if (featurep 'ns) |
| 477 | (bindings--define-key menu [spell] | 482 | (bindings--define-key menu [spell] |
diff --git a/src/androidmenu.c b/src/androidmenu.c index 7b27825ad60..acad775f26a 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c | |||
| @@ -98,7 +98,7 @@ android_init_emacs_context_menu (void) | |||
| 98 | FIND_METHOD_STATIC (create_context_menu, "createContextMenu", | 98 | FIND_METHOD_STATIC (create_context_menu, "createContextMenu", |
| 99 | "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); | 99 | "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); |
| 100 | 100 | ||
| 101 | FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;Z)V"); | 101 | FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ)V"); |
| 102 | FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;" | 102 | FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;" |
| 103 | "Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); | 103 | "Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); |
| 104 | FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V"); | 104 | FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V"); |
| @@ -241,7 +241,7 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, | |||
| 241 | Lisp_Object pane_name, prefix; | 241 | Lisp_Object pane_name, prefix; |
| 242 | const char *pane_string; | 242 | const char *pane_string; |
| 243 | specpdl_ref count, count1; | 243 | specpdl_ref count, count1; |
| 244 | Lisp_Object item_name, enable, def, tem, entry; | 244 | Lisp_Object item_name, enable, def, tem, entry, type, selected; |
| 245 | jmethodID method; | 245 | jmethodID method; |
| 246 | jobject store; | 246 | jobject store; |
| 247 | bool rc; | 247 | bool rc; |
| @@ -250,6 +250,7 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, | |||
| 250 | struct android_dismiss_menu_data data; | 250 | struct android_dismiss_menu_data data; |
| 251 | struct android_menu_subprefix *subprefix, *temp_subprefix; | 251 | struct android_menu_subprefix *subprefix, *temp_subprefix; |
| 252 | struct android_menu_subprefix *subprefix_1; | 252 | struct android_menu_subprefix *subprefix_1; |
| 253 | bool checkmark; | ||
| 253 | 254 | ||
| 254 | count = SPECPDL_INDEX (); | 255 | count = SPECPDL_INDEX (); |
| 255 | 256 | ||
| @@ -351,6 +352,8 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, | |||
| 351 | item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); | 352 | item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); |
| 352 | enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); | 353 | enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); |
| 353 | def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); | 354 | def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); |
| 355 | type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE); | ||
| 356 | selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED); | ||
| 354 | 357 | ||
| 355 | /* This is an actual menu item (or submenu). Add it to the | 358 | /* This is an actual menu item (or submenu). Add it to the |
| 356 | menu. */ | 359 | menu. */ |
| @@ -392,12 +395,20 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, | |||
| 392 | title_string = (!NILP (item_name) | 395 | title_string = (!NILP (item_name) |
| 393 | ? android_build_string (item_name) | 396 | ? android_build_string (item_name) |
| 394 | : NULL); | 397 | : NULL); |
| 398 | |||
| 399 | /* Determine whether or not to display a check box. */ | ||
| 400 | |||
| 401 | checkmark = (EQ (type, QCtoggle) | ||
| 402 | || EQ (type, QCradio)); | ||
| 403 | |||
| 395 | (*android_java_env)->CallVoidMethod (android_java_env, | 404 | (*android_java_env)->CallVoidMethod (android_java_env, |
| 396 | current_context_menu, | 405 | current_context_menu, |
| 397 | menu_class.add_item, | 406 | menu_class.add_item, |
| 398 | (jint) item_id, | 407 | (jint) item_id, |
| 399 | title_string, | 408 | title_string, |
| 400 | (jboolean) !NILP (enable)); | 409 | (jboolean) !NILP (enable), |
| 410 | (jboolean) checkmark, | ||
| 411 | (jboolean) !NILP (selected)); | ||
| 401 | android_exception_check (); | 412 | android_exception_check (); |
| 402 | 413 | ||
| 403 | if (title_string) | 414 | if (title_string) |
diff --git a/src/menu.c b/src/menu.c index e02ee880119..6ab34a16996 100644 --- a/src/menu.c +++ b/src/menu.c | |||
| @@ -48,7 +48,7 @@ static bool | |||
| 48 | have_boxes (void) | 48 | have_boxes (void) |
| 49 | { | 49 | { |
| 50 | #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \ | 50 | #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \ |
| 51 | || defined (HAVE_HAIKU) | 51 | || defined (HAVE_HAIKU) || defined (HAVE_ANDROID) |
| 52 | if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))) | 52 | if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))) |
| 53 | return 1; | 53 | return 1; |
| 54 | #endif | 54 | #endif |