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.
This commit is contained in:
Po Lu 2023-01-28 16:29:22 +08:00
parent 5bd38905ac
commit 198b8160cf
8 changed files with 123 additions and 16 deletions

View 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

View 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"/>

View 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.

View file

@ -56,7 +56,7 @@ private class Item implements MenuItem.OnMenuItemClickListener
public int itemID;
public String itemName;
public EmacsContextMenu subMenu;
public boolean isEnabled;
public boolean isEnabled, isCheckable, isChecked;
@Override
public boolean
@ -108,10 +108,15 @@ private class Item implements MenuItem.OnMenuItemClickListener
/* 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 @@ private class Item implements MenuItem.OnMenuItemClickListener
item.itemID = itemID;
item.itemName = itemName;
item.isEnabled = isEnabled;
item.isCheckable = isCheckable;
item.isChecked = isChecked;
menuItems.add (item);
}
@ -198,6 +205,15 @@ private class Item implements MenuItem.OnMenuItemClickListener
/* 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);
}
}
}

View 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);

View file

@ -472,6 +472,11 @@
(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]

View 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)

View 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