]> git.eshelyaron.com Git - emacs.git/commitdiff
Update Android port
authorPo Lu <luangruo@yahoo.com>
Sat, 28 Jan 2023 08:29:22 +0000 (16:29 +0800)
committerPo Lu <luangruo@yahoo.com>
Sat, 28 Jan 2023 08:29:22 +0000 (16:29 +0800)
* 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.

doc/emacs/android.texi
java/AndroidManifest.xml.in
java/README
java/org/gnu/emacs/EmacsContextMenu.java
java/org/gnu/emacs/EmacsCopyArea.java
lisp/menu-bar.el
src/androidmenu.c
src/menu.c

index 98d7f1e1d9e1fa526ddc25a6f05c04a24f10fef0..ae4080994b883b9f8ccb17d82cd63d2fe48d84f0 100644 (file)
@@ -177,17 +177,27 @@ makes the system more secure.  Unfortunately, it also means that Emacs
 cannot access files in those directories, despite holding the
 necessary permissions.  Thankfully, the Open Handset Alliance's
 version of Android allows this restriction to be disabled on a
-per-program basis; the corresponding option in the system settings
-panel is:
+per-program basis; on Android 10, the corresponding option in the
+system settings panel is:
 
 @indentedblock
 System -> Developer Options -> App Compatibility Changes -> Emacs ->
 DEFAULT_SCOPED_STORAGE
 @end indentedblock
 
-  After you disable this setting and grant Emacs the ``Files and
-Media'' permission, it will be able to access files under
-@file{/sdcard} as usual.
+  And on Android 11 and later, the corresponding option in the systems
+settings panel is:
+
+@indentedblock
+System -> Apps -> Special App Access -> All files access -> Emacs
+@end indentedblock
+
+  After you disable or enable this setting as appropriate and grant
+Emacs the ``Files and Media'' permission, it will be able to access
+files under @file{/sdcard} as usual.
+
+  These settings are not present on many proprietary versions of
+Android.
 
 @node Android Environment
 @section Running Emacs under Android
index 527ce74c474821fbdc15e4c8953cadc852f3f740..09e4e788e0b5282c456bdf3915c923efa6c41b03 100644 (file)
@@ -52,6 +52,10 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>. -->
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   <uses-permission android:name="android.permission.CAMERA" />
 
+  <!-- This is required on Android 11 or later to access /sdcard.  -->
+
+  <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+
   <uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@"
            android:targetSdkVersion="33"/>
 
index 44f5a415162f3137cc791bf97bdc7ef9aacc4fb0..3bce2556403f166246d9ac69d2d2639ba0a07235 100644 (file)
@@ -292,15 +292,15 @@ public class EmacsFrobinicator
   }
 }
 
-Java arrays are similar to C arrays in that they can not grow. But
+Java arrays are similar to C arrays in that they can not grow.  But
 they are very much unlike C arrays in that they are always references
-(as opposed to decaying into pointers in various situations), and
+(as opposed to decaying into pointers in only some situations), and
 contain information about their length.
 
 If another function named ``frobinicate1'' takes an array as an
 argument, then it need not take the length of the array.
 
-Instead, it simply iterates over the array like so:
+Instead, it may simply iterate over the array like so:
 
 int i, k;
 
@@ -339,10 +339,65 @@ struct emacs_array_container
 
 or, possibly even better,
 
-typedef int my_array[10];
+typedef int emacs_array_container[10];
 
 Alas, Java has no equivalent of `typedef'.
 
+Like in C, Java string literals are delimited by double quotes.
+Unlike C, however, strings are not NULL-terminated arrays of
+characters, but a distinct type named ``String''.  They store their
+own length, characters in Java's 16-bit ``char'' type, and are capable
+of holding NULL bytes.
+
+Instead of writing:
+
+wchar_t character;
+extern char *s;
+size_t s;
+
+  for (/* determine n, s in a loop.  */)
+    s += mbstowc (&character, s, n);
+
+or:
+
+const char *byte;
+
+for (byte = my_string; *byte; ++byte)
+  /* do something with *byte.  */;
+
+or perhaps even:
+
+size_t length, i;
+char foo;
+
+length = strlen (my_string);
+
+for (i = 0; i < length; ++i)
+  foo = my_string[i];
+
+you write:
+
+char foo;
+int i;
+
+for (i = 0; i < myString.length (); ++i)
+  foo = myString.charAt (0);
+
+Java also has stricter rules on what can be used as a truth value in a
+conditional.  While in C, any non-zero value is true, Java requires
+that every truth value be of the boolean type ``boolean''.
+
+What this means is that instead of simply writing:
+
+  if (foo || bar)
+
+where foo can either be 1 or 0, and bar can either be NULL or a
+pointer to something, you must explicitly write:
+
+  if (foo != 0 || bar != null)
+
+in Java.
+
 JAVA NATIVE INTERFACE
 
 Java also provides an interface for C code to interface with Java.
index 056d8fb692c336b355ef18a6960a7f6d6080a4b5..92429410d032bf4c849a46610ad5a62e1adec51f 100644 (file)
@@ -56,7 +56,7 @@ public class EmacsContextMenu
     public int itemID;
     public String itemName;
     public EmacsContextMenu subMenu;
-    public boolean isEnabled;
+    public boolean isEnabled, isCheckable, isChecked;
 
     @Override
     public boolean
@@ -108,10 +108,15 @@ public class EmacsContextMenu
 
   /* Add a normal menu item to the context menu with the id ITEMID and
      the name ITEMNAME.  Enable it if ISENABLED, else keep it
-     disabled.  */
+     disabled.
+
+     If this is not a submenu and ISCHECKABLE is set, make the item
+     checkable.  Likewise, if ISCHECKED is set, make the item
+     checked.  */
 
   public void
-  addItem (int itemID, String itemName, boolean isEnabled)
+  addItem (int itemID, String itemName, boolean isEnabled,
+          boolean isCheckable, boolean isChecked)
   {
     Item item;
 
@@ -119,6 +124,8 @@ public class EmacsContextMenu
     item.itemID = itemID;
     item.itemName = itemName;
     item.isEnabled = isEnabled;
+    item.isCheckable = isCheckable;
+    item.isChecked = isChecked;
 
     menuItems.add (item);
   }
@@ -198,6 +205,15 @@ public class EmacsContextMenu
            /* If the item ID is zero, then disable the item.  */
            if (item.itemID == 0 || !item.isEnabled)
              menuItem.setEnabled (false);
+
+           /* Now make the menu item display a checkmark as
+              appropriate.  */
+
+           if (item.isCheckable)
+             menuItem.setCheckable (true);
+
+           if (item.isChecked)
+             menuItem.setChecked (true);
          }
       }
   }
index 7a97d7067948a20f49080e6742f4faaa62da7515..f8974e17c2e2f0101fc0409b7339d3fdfd5ad7a9 100644 (file)
@@ -99,6 +99,12 @@ public class EmacsCopyArea
     if (src_y + height > srcBitmap.getHeight ())
       height = srcBitmap.getHeight () - src_y;
 
+    /* If width and height are empty or negative, then skip the entire
+       CopyArea operation lest createBitmap throw an exception.  */
+
+    if (width <= 0 || height <= 0)
+      return;
+
     rect = new Rect (dest_x, dest_y, dest_x + width,
                     dest_y + height);
 
index d020cf6e90acae1cd4c931433e08ddbeb7089b95..2d907fb38274ec50292446472f8d647fd71861c3 100644 (file)
 (defvar menu-bar-edit-menu
   (let ((menu (make-sparse-keymap "Edit")))
 
+    (bindings--define-key menu [execute-extended-command]
+      '(menu-item "Execute Command" execute-extended-command
+                  :enable t
+                  :help "Read a command name, its arguments, then call it."))
+
     ;; ns-win.el said: Add spell for platform consistency.
     (if (featurep 'ns)
         (bindings--define-key menu [spell]
index 7b27825ad60d42cff588e070e19c899826ad1656..acad775f26aea7f98a51608e53bfbff709e0c982 100644 (file)
@@ -98,7 +98,7 @@ android_init_emacs_context_menu (void)
   FIND_METHOD_STATIC (create_context_menu, "createContextMenu",
                      "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;");
 
-  FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;Z)V");
+  FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ)V");
   FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;"
               "Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;");
   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,
   Lisp_Object pane_name, prefix;
   const char *pane_string;
   specpdl_ref count, count1;
-  Lisp_Object item_name, enable, def, tem, entry;
+  Lisp_Object item_name, enable, def, tem, entry, type, selected;
   jmethodID method;
   jobject store;
   bool rc;
@@ -250,6 +250,7 @@ android_menu_show (struct frame *f, int x, int y, int menuflags,
   struct android_dismiss_menu_data data;
   struct android_menu_subprefix *subprefix, *temp_subprefix;
   struct android_menu_subprefix *subprefix_1;
+  bool checkmark;
 
   count = SPECPDL_INDEX ();
 
@@ -351,6 +352,8 @@ android_menu_show (struct frame *f, int x, int y, int menuflags,
          item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
          enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
          def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+         type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+         selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
 
          /* This is an actual menu item (or submenu).  Add it to the
             menu.  */
@@ -392,12 +395,20 @@ android_menu_show (struct frame *f, int x, int y, int menuflags,
              title_string = (!NILP (item_name)
                              ? android_build_string (item_name)
                              : NULL);
+
+             /* Determine whether or not to display a check box.  */
+
+             checkmark = (EQ (type, QCtoggle)
+                          || EQ (type, QCradio));
+
              (*android_java_env)->CallVoidMethod (android_java_env,
                                                   current_context_menu,
                                                   menu_class.add_item,
                                                   (jint) item_id,
                                                   title_string,
-                                                  (jboolean) !NILP (enable));
+                                                  (jboolean) !NILP (enable),
+                                                  (jboolean) checkmark,
+                                                  (jboolean) !NILP (selected));
              android_exception_check ();
 
              if (title_string)
index e02ee8801190c1bee504d73c5a80ab0b3808ed26..6ab34a16996a109481a7c1ff720d8e831f7c53a3 100644 (file)
@@ -48,7 +48,7 @@ static bool
 have_boxes (void)
 {
 #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \
-  || defined (HAVE_HAIKU)
+  || defined (HAVE_HAIKU) || defined (HAVE_ANDROID)
   if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)))
     return 1;
 #endif