emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android 363e293cc91: Update Android port


From: Po Lu
Subject: feature/android 363e293cc91: Update Android port
Date: Thu, 15 Jun 2023 00:37:50 -0400 (EDT)

branch: feature/android
commit 363e293cc919ab02c40bd9a8fa4875c2e5644b2d
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Update Android port
    
    * java/org/gnu/emacs/EmacsInputConnection.java
    (EmacsInputConnection, beginBatchEdit, reset, endBatchEdit):
    Keep track of the number of batch edits and return an
    appropriate value.
    (takeSnapshot): Implement function.
    * java/org/gnu/emacs/EmacsNative.java (takeSnapshot): New
    function.
    * java/org/gnu/emacs/EmacsService.java (resetIC): Improve
    debugging output.
    * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
    Call `reset' to clear the UI side batch edit count.
    * src/androidterm.c (struct
    android_get_surrounding_text_context): New fields
    `conversion_start' and `conversion_end'.
    (android_get_surrounding_text): Return the conversion region.
    (android_get_surrounding_text_internal, NATIVE_NAME): Factor out
    `getSurroundingText'.
    (takeSnapshot): New function.
---
 java/org/gnu/emacs/EmacsInputConnection.java |  66 +++++++++++---
 java/org/gnu/emacs/EmacsNative.java          |   2 +
 java/org/gnu/emacs/EmacsService.java         |  25 +++++-
 java/org/gnu/emacs/EmacsView.java            |   3 +
 src/androidterm.c                            | 129 +++++++++++++++++++++++++--
 5 files changed, 201 insertions(+), 24 deletions(-)

diff --git a/java/org/gnu/emacs/EmacsInputConnection.java 
b/java/org/gnu/emacs/EmacsInputConnection.java
index 1bcc9a62a81..f8dce5dfa79 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -50,6 +50,11 @@ public final class EmacsInputConnection implements 
InputConnection
   /* The handle ID associated with that view's window.  */
   private short windowHandle;
 
+  /* Number of batch edits currently underway.  Used to avoid
+     synchronizing with the Emacs thread after each
+     `endBatchEdit'.  */
+  private int batchEditCount;
+
   /* Whether or not to synchronize and call `updateIC' with the
      selection position after committing text.
 
@@ -110,6 +115,10 @@ public final class EmacsInputConnection implements 
InputConnection
       Log.d (TAG, "beginBatchEdit");
 
     EmacsNative.beginBatchEdit (windowHandle);
+
+    /* Keep a record of the number of outstanding batch edits here as
+       well.  */
+    batchEditCount++;
     return true;
   }
 
@@ -125,7 +134,14 @@ public final class EmacsInputConnection implements 
InputConnection
       Log.d (TAG, "endBatchEdit");
 
     EmacsNative.endBatchEdit (windowHandle);
-    return true;
+
+    /* Subtract one from the UI thread record of the number of batch
+       edits currently under way.  */
+
+    if (batchEditCount > 0)
+      batchEditCount -= 1;
+
+    return batchEditCount > 0;
   }
 
   public boolean
@@ -584,21 +600,50 @@ public final class EmacsInputConnection implements 
InputConnection
     return text;
   }
 
-
-  /* Override functions which are not implemented.  */
-
   @Override
-  public Handler
-  getHandler ()
+  public TextSnapshot
+  takeSnapshot ()
   {
-    return null;
+    TextSnapshot snapshot;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    snapshot = EmacsNative.takeSnapshot (windowHandle);
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, ("takeSnapshot: "
+                  + snapshot.getSurroundingText ().getText ()
+                  + " @ " + snapshot.getCompositionEnd ()
+                  + ", " + snapshot.getCompositionStart ()));
+
+    return snapshot;
   }
 
   @Override
   public void
   closeConnection ()
   {
+    batchEditCount = 0;
+  }
+
+
 
+  public void
+  reset ()
+  {
+    batchEditCount = 0;
+  }
+
+
+  /* Override functions which are not implemented.  */
+
+  @Override
+  public Handler
+  getHandler ()
+  {
+    return null;
   }
 
   @Override
@@ -616,13 +661,6 @@ public final class EmacsInputConnection implements 
InputConnection
     return false;
   }
 
-  @Override
-  public TextSnapshot
-  takeSnapshot ()
-  {
-    return null;
-  }
-
   @Override
   public boolean
   clearMetaKeyStates (int states)
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index 2fcbf8b94ef..9e87c419f95 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -26,6 +26,7 @@ import android.graphics.Bitmap;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextSnapshot;
 
 public final class EmacsNative
 {
@@ -230,6 +231,7 @@ public final class EmacsNative
   public static native SurroundingText getSurroundingText (short window,
                                                           int left, int right,
                                                           int flags);
+  public static native TextSnapshot takeSnapshot (short window);
 
 
   /* Return the current value of the selection, or -1 upon
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 96216e51cf4..2fe4e8c4146 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -767,8 +767,31 @@ public final class EmacsService extends Service
   public void
   resetIC (EmacsWindow window, int icMode)
   {
+    int oldMode;
+
     if (DEBUG_IC)
-      Log.d (TAG, "resetIC: " + window);
+      Log.d (TAG, "resetIC: " + window + ", " + icMode);
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+       && (oldMode = window.view.getICMode ()) == icMode
+       /* Don't do this if there is currently no input
+          connection.  */
+       && oldMode != IC_MODE_NULL)
+      {
+       if (DEBUG_IC)
+         Log.d (TAG, "resetIC: calling invalidateInput");
+
+       /* Android 33 and later allow the IM reset to be optimized out
+          and replaced by a call to `invalidateInput', which is much
+          faster, as it does not involve resetting the input
+          connection.  */
+
+       icBeginSynchronous ();
+       window.view.imManager.invalidateInput (window.view);
+       icEndSynchronous ();
+
+       return;
+      }
 
     window.view.setICMode (icMode);
 
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 278c6025902..aba1184b0c2 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -681,6 +681,9 @@ public final class EmacsView extends ViewGroup
 
     if (inputConnection == null)
       inputConnection = new EmacsInputConnection (this);
+    else
+      /* Clear several pieces of state in the input connection.  */
+      inputConnection.reset ();
 
     /* Return the input connection.  */
     return inputConnection;
diff --git a/src/androidterm.c b/src/androidterm.c
index 191ff65199b..29076981a47 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -5668,6 +5668,10 @@ struct android_get_surrounding_text_context
   /* Offsets into that text.  */
   ptrdiff_t offset, start, end;
 
+  /* The start and end indices of the conversion region.
+     -1 if it does not exist.  */
+  ptrdiff_t conversion_start, conversion_end;
+
   /* The window.  */
   android_window window;
 };
@@ -5706,22 +5710,47 @@ android_get_surrounding_text (void *data)
       request->start = request->end;
       request->end = temp;
     }
+
+  /* Retrieve the conversion region.  */
+
+  request->conversion_start = -1;
+  request->conversion_end = -1;
+
+  if (MARKERP (f->conversion.compose_region_start))
+    {
+      request->conversion_start
+       = marker_position (f->conversion.compose_region_start) - 1;
+      request->conversion_end
+       = marker_position (f->conversion.compose_region_end) - 1;
+    }
 }
 
-JNIEXPORT jobject JNICALL
-NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object,
-                                 jshort window, jint before_length,
-                                 jint after_length, jint flags)
-{
-  JNI_STACK_ALIGNMENT_PROLOGUE;
+/* Return a local reference to a `SurroundingText' object describing
+   WINDOW's surrounding text.  ENV should be a valid JNI environment
+   for the current thread.
 
-  static jclass class;
-  static jmethodID constructor;
+   BEFORE_LENGTH and AFTER_LENGTH specify the number of characters
+   around point and mark to return.
 
+   Return the conversion region (or -1) in *CONVERSION_START and
+   *CONVERSION_END if non-NULL.
+
+   Value is the object upon success, else NULL.  */
+
+static jobject
+android_get_surrounding_text_internal (JNIEnv *env, jshort window,
+                                      jint before_length,
+                                      jint after_length,
+                                      ptrdiff_t *conversion_start,
+                                      ptrdiff_t *conversion_end)
+{
   struct android_get_surrounding_text_context context;
   jstring string;
   jobject object;
 
+  static jclass class;
+  static jmethodID constructor;
+
   /* Initialize CLASS if it has not yet been initialized.  */
 
   if (!class)
@@ -5745,7 +5774,9 @@ NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject 
ignored_object,
 
       class = (*env)->NewGlobalRef (env, class);
       if (!class)
-       return NULL;
+       /* Clear class to prevent a local reference from remaining in
+          `class'.  */
+       return (class = NULL);
 
       /* Now look for its constructor.  */
       constructor = (*env)->GetMethodID (env, class, "<init>",
@@ -5787,6 +5818,86 @@ NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject 
ignored_object,
   if (!object)
     return NULL;
 
+  /* Now return the conversion region if that was requested.  */
+
+  if (conversion_start)
+    {
+      *conversion_start = context.conversion_start;
+      *conversion_end = context.conversion_start;
+    }
+
+  return object;
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject object,
+                                 jshort window, jint before_length,
+                                 jint after_length, jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  return android_get_surrounding_text_internal (env, window, before_length,
+                                               after_length, NULL, NULL);
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (takeSnapshot) (JNIEnv *env, jobject object, jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  jobject text;
+  ptrdiff_t start, end;
+
+  static jclass class;
+  static jmethodID constructor;
+
+  /* First, obtain the surrounding text and conversion region.  */
+  text = android_get_surrounding_text_internal (env, window, 600, 600,
+                                               &start, &end);
+
+  /* If that fails, return NULL.  */
+
+  if (!text)
+    return NULL;
+
+  /* Next, initialize the TextSnapshot class.  */
+
+  if (!class)
+    {
+      class
+       = (*env)->FindClass (env, ("android/view/inputmethod"
+                                  "/TextSnapshot"));
+#if __ANDROID_API__ < 33
+      /* If CLASS cannot be found, the version of Android currently
+        running is too old.  */
+
+      if (!class)
+       {
+         (*env)->ExceptionClear (env);
+         return NULL;
+       }
+#else /* __ANDROID_API__ >= 33 */
+      assert (class);
+#endif /* __ANDROID_API__ < 33 */
+
+      class = (*env)->NewGlobalRef (env, class);
+      if (!class)
+       /* Clear class to prevent a local reference from remaining in
+          `class'.  */
+       return (class = NULL);
+
+      constructor = (*env)->GetMethodID (env, class, "<init>",
+                                        "(Landroid/view/inputmethod"
+                                        "/SurroundingText;III)V");
+      assert (constructor);
+    }
+
+  /* Try to create a TextSnapshot object.  */
+  eassert (start <= end);
+  object = (*env)->NewObject (env, class, constructor, text,
+                             (jint) min (start, TYPE_MAXIMUM (jint)),
+                             (jint) min (end, TYPE_MAXIMUM (jint)),
+                             (jint) 0);
   return object;
 }
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]