From: Po Lu Date: Wed, 8 Mar 2023 07:04:49 +0000 (+0800) Subject: Update Android port X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=bb55528c7b58c5f50336ed3f2ff9759559d78680;p=emacs.git Update Android port * doc/emacs/android.texi (Android File System): Document what `temp~unlinked' means in the temporary files directory. * java/org/gnu/emacs/EmacsService.java (updateExtractedText): New function. * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): Ask the input method nicely to not display the extracted text UI. * src/android.c (struct android_emacs_service): New method `updateExtractedText'. (android_hack_asset_fd_fallback): Improve naming convention. Fix typo. (android_init_emacs_service): Add new method. (android_update_extracted_text): New function. (android_open_asset): Fix typo. * src/androidgui.h: Update prototypes. * src/androidterm.c (struct android_get_extracted_text_context): New field `flags'. (android_get_extracted_text): Set flags on the frame's output data. (android_build_extracted_text): New function. (getExtractedText): Move out class structures. (android_update_selection): Send updates to extracted text if the input method asked for them. (android_reset_conversion): Clear extracted text flags. * src/androidterm.h (struct android_output): New fields for storing extracted text data. --- diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index d49e0754b0a..8e98b92314a 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -205,6 +205,13 @@ other directories are not found at any fixed location, although the app data directory is typically symlinked to @file{/data/data/org.gnu.emacs}. +@cindex temp~unlinked.NNNN files, Android + On Android devices running very old (2.6.29) versions of the Linux +kernel, Emacs needs to create files named starting with +@file{temp~unlinked} in the the temporary file directory in order to +read from asset files. Do not create files with such names yourself, +or they may be overwritten or removed. + @cindex file system limitations, Android 11 On Android 11 and later, the Android system restricts applications from accessing files in the @file{/sdcard} directory using diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index f99d7a40067..848ad4de789 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -29,6 +29,7 @@ import android.graphics.Point; import android.view.InputDevice; import android.view.KeyEvent; +import android.view.inputmethod.ExtractedText; import android.app.Notification; import android.app.NotificationManager; @@ -811,4 +812,15 @@ public final class EmacsService extends Service } }); } + + public void + updateExtractedText (EmacsWindow window, ExtractedText text, + int token) + { + if (DEBUG_IC) + Log.d (TAG, "updateExtractedText: @" + token + ", " + text); + + window.view.imManager.updateExtractedText (window.view, + token, text); + } }; diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 90a2c912a5a..6ace609f386 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -569,6 +569,7 @@ public final class EmacsView extends ViewGroup /* Make sure the input method never displays a full screen input box that obscures Emacs. */ info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; + info.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI; /* Set a reasonable inputType. */ info.inputType = InputType.TYPE_CLASS_TEXT; diff --git a/src/android.c b/src/android.c index 11b0fa5e0f3..e620a041348 100644 --- a/src/android.c +++ b/src/android.c @@ -112,6 +112,7 @@ struct android_emacs_service jmethodID check_content_uri; jmethodID query_battery; jmethodID display_toast; + jmethodID update_extracted_text; }; struct android_emacs_pixmap @@ -1236,13 +1237,12 @@ android_hack_asset_fd_fallback (AAsset *asset) Creating an ashmem file descriptor and reading from it doesn't work on these old Android versions. */ - snprintf (filename, PATH_MAX, "%s/%s.%d", - android_cache_dir, "temp-unlinked", - getpid ()); + snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d", + android_cache_dir, getpid ()); fd = open (filename, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); - if (fd < 1) + if (fd < 0) return -1; if (unlink (filename)) @@ -2135,6 +2135,9 @@ android_init_emacs_service (void) FIND_METHOD (query_battery, "queryBattery", "()[J"); FIND_METHOD (display_toast, "displayToast", "(Ljava/lang/String;)V"); + FIND_METHOD (update_extracted_text, "updateExtractedText", + "(Lorg/gnu/emacs/EmacsWindow;" + "Landroid/view/inputmethod/ExtractedText;I)V"); #undef FIND_METHOD } @@ -5991,6 +5994,37 @@ android_reset_ic (android_window window, enum android_ic_mode mode) android_exception_check (); } +/* Make updates to extracted text known to the input method on + WINDOW. TEXT should be a local reference to the new + extracted text. TOKEN should be the token specified by the + input method. */ + +void +android_update_extracted_text (android_window window, void *text, + int token) +{ + jobject object; + jmethodID method; + + object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + method = service_class.update_extracted_text; + + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + method, object, + /* N.B. that + text is not + jobject, + because that + type is not + available in + androidgui.h. */ + (jobject) text, + (jint) token); + android_exception_check_1 (text); +} + /* Window decoration management functions. */ @@ -6083,7 +6117,7 @@ android_open_asset (const char *filename, int oflag, mode_t mode) get a regular file descriptor. */ fd.fd = android_open (filename, oflag, mode); - if (fd.fd < 1) + if (fd.fd < 0) return fd; /* Set fd.asset to NULL, signifying that it is a file diff --git a/src/androidgui.h b/src/androidgui.h index e1c80a71a59..afcaed98cae 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -613,6 +613,8 @@ extern int android_wc_lookup_string (android_key_pressed_event *, extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t); extern void android_reset_ic (android_window, enum android_ic_mode); +extern void android_update_extracted_text (android_window, void *, + int); extern int android_set_fullscreen (android_window, bool); #endif diff --git a/src/androidterm.c b/src/androidterm.c index 0cc2b35099c..f4a535292f2 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -4968,6 +4968,10 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, android_write_event (&event); } + + +/* Text extraction. */ + struct android_get_extracted_text_context { /* The parameters of the request. */ @@ -4976,6 +4980,9 @@ struct android_get_extracted_text_context /* Token for the request. */ int token; + /* Flags associated with the request. */ + int flags; + /* The returned text, or NULL. */ char *text; @@ -5011,6 +5018,14 @@ android_get_extracted_text (void *data) = get_extracted_text (f, min (request->hint_max_chars, 600), &request->start, &request->offset, &request->length, &request->bytes); + + /* See if request->flags & GET_EXTRACTED_TEXT_MONITOR. If so, then + the input method has asked to monitor changes to the extracted + text until the next IM context reset. */ + + FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = request->flags; + FRAME_ANDROID_OUTPUT (f)->extracted_text_token = request->token; + FRAME_ANDROID_OUTPUT (f)->extracted_text_hint = request->hint_max_chars; } /* Structure describing the `ExtractedTextRequest' class. @@ -5038,6 +5053,51 @@ struct android_extracted_text_class jfieldID text; }; +/* Fields and methods associated with the `ExtractedTextRequest' + class. */ +struct android_extracted_text_request_class request_class; + +/* Fields and methods associated with the `ExtractedText' class. */ +struct android_extracted_text_class text_class; + +/* Return an ExtractedText object corresponding to the extracted text + TEXT. START is a character position describing the offset of the + first character in TEXT. OFFSET is the offset of point relative to + START. + + Assume that request_class and text_class have already been + initialized. + + Value is NULL if an error occurs; the exception is not cleared, + else a local reference to the ExtractedText object. */ + +static jobject +android_build_extracted_text (jstring text, ptrdiff_t start, + ptrdiff_t offset) +{ + JNIEnv *env; + jobject object; + + env = android_java_env; + + /* Create an ExtractedText object containing this information. */ + object = (*env)->NewObject (env, text_class.class, + text_class.constructor); + if (!object) + return NULL; + + (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); + (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); + (*env)->SetIntField (env, object, text_class.selection_start, + min (offset, TYPE_MAXIMUM (jint))); + (*env)->SetIntField (env, object, text_class.selection_end, + min (offset, TYPE_MAXIMUM (jint))); + (*env)->SetIntField (env, object, text_class.start_offset, + min (start, TYPE_MAXIMUM (jint))); + (*env)->SetObjectField (env, object, text_class.text, text); + return object; +} + JNIEXPORT jobject JNICALL NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, jshort window, jobject request, @@ -5046,14 +5106,10 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, JNI_STACK_ALIGNMENT_PROLOGUE; struct android_get_extracted_text_context context; - static struct android_extracted_text_request_class request_class; - static struct android_extracted_text_class text_class; jstring string; jclass class; jobject object; - /* TODO: report changes to extracted text. */ - /* Initialize both classes if necessary. */ if (!request_class.initialized) @@ -5106,6 +5162,7 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, = (*env)->GetIntField (env, request, request_class.hint_max_chars); context.token = (*env)->GetIntField (env, request, request_class.token); + context.flags = flags; context.text = NULL; context.window = window; @@ -5126,8 +5183,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, return NULL; /* Create an ExtractedText object containing this information. */ - object = (*android_java_env)->NewObject (env, text_class.class, - text_class.constructor); + object = (*env)->NewObject (env, text_class.class, + text_class.constructor); if (!object) return NULL; @@ -5143,6 +5200,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, return object; } + + JNIEXPORT jstring JNICALL NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object, jshort window) @@ -5210,8 +5269,12 @@ NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object, static void android_update_selection (struct frame *f, struct window *w) { - ptrdiff_t start, end, point, mark; + ptrdiff_t start, end, point, mark, offset, length, bytes; struct buffer *b; + int hint, token; + char *text; + jobject extracted; + jstring string; if (MARKERP (f->conversion.compose_region_start)) { @@ -5246,6 +5309,36 @@ android_update_selection (struct frame *f, struct window *w) the selection is less than or equal to the end. */ android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark), max (point, mark), start, end); + + /* Update the extracted text as well, if the input method has asked + for updates. 1 is + InputConnection.GET_EXTRACTED_TEXT_MONITOR. */ + + if (FRAME_ANDROID_OUTPUT (f)->extracted_text_flags & 1) + { + hint = FRAME_ANDROID_OUTPUT (f)->extracted_text_hint; + token = FRAME_ANDROID_OUTPUT (f)->extracted_text_token; + text = get_extracted_text (f, min (hint, 600), &start, + &offset, &length, &bytes); + + /* Make a string out of the extracted text. */ + string = android_text_to_string (android_java_env, + text, length, bytes); + xfree (text); + android_exception_check (); + + /* Make extracted text out of that string. */ + extracted = android_build_extracted_text (string, start, + offset); + android_exception_check_1 (string); + ANDROID_DELETE_LOCAL_REF (string); + + /* extracted is now an associated ExtractedText object. Perform + the update. */ + android_update_extracted_text (FRAME_ANDROID_WINDOW (f), + extracted, token); + ANDROID_DELETE_LOCAL_REF (extracted); + } } /* Notice that the input method connection to F should be reset as a @@ -5283,6 +5376,10 @@ android_reset_conversion (struct frame *f) android_reset_ic (FRAME_ANDROID_WINDOW (f), mode); + /* Clear extracted text flags. Since the IM has been reinitialised, + it should no longer be displaying extracted text. */ + FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = 0; + /* Move its selection to the specified position. */ android_update_selection (f, NULL); } diff --git a/src/androidterm.h b/src/androidterm.h index ac845187a66..9bd11bb7853 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -241,6 +241,16 @@ struct android_output /* List of all tools (either styluses or fingers) pressed onto the frame. */ struct android_touch_point *touch_points; + + /* Flags associated with the last request to obtain ``extracted + text''. */ + int extracted_text_flags; + + /* Token asssociated with that request. */ + int extracted_text_token; + + /* The number of characters of extracted text wanted by the IM. */ + int extracted_text_hint; }; enum