]> git.eshelyaron.com Git - emacs.git/commitdiff
Update Android port
authorPo Lu <luangruo@yahoo.com>
Wed, 8 Mar 2023 07:04:49 +0000 (15:04 +0800)
committerPo Lu <luangruo@yahoo.com>
Wed, 8 Mar 2023 07:04:49 +0000 (15:04 +0800)
* 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.

doc/emacs/android.texi
java/org/gnu/emacs/EmacsService.java
java/org/gnu/emacs/EmacsView.java
src/android.c
src/androidgui.h
src/androidterm.c
src/androidterm.h

index d49e0754b0a77c1358f7f82fc782b9db8295c4b5..8e98b92314ab23a61139d97bd249ec95091812df 100644 (file)
@@ -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
index f99d7a4006716ef968e18fdd3558a0d1d7f5441e..848ad4de789e37db0106c86f0995261032454739 100644 (file)
@@ -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);
+  }
 };
index 90a2c912a5ad4b0bfd5b516e9000f5fd1914bc8a..6ace609f3869a03e3ef2cded11de7baa4bcd8124 100644 (file)
@@ -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;
index 11b0fa5e0f358b16aa00609e0a44983aa98f4baf..e620a041348ec91d0b282ea615f8ed389c2d1188 100644 (file)
@@ -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);
+}
+
 \f
 
 /* 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
index e1c80a71a592a3cdb5640b1ee1cdfc4cb1d403ac..afcaed98cae3d65957507b67445dbc7c6f1f251d 100644 (file)
@@ -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
index 0cc2b35099c9312376541ee3750dc71d01843f7f..f4a535292f233d8ef86622ed03b140d4038b535e 100644 (file)
@@ -4968,6 +4968,10 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object,
   android_write_event (&event);
 }
 
+\f
+
+/* 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;
 }
 
+\f
+
 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);
 }
index ac845187a665cbacd4434bfaab90b729738ebcc0..9bd11bb7853c143437301ffbad0b014b4514dc0b 100644 (file)
@@ -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