]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve IM synchronization on Android
authorPo Lu <luangruo@yahoo.com>
Wed, 14 Jun 2023 07:37:47 +0000 (15:37 +0800)
committerPo Lu <luangruo@yahoo.com>
Wed, 14 Jun 2023 07:37:47 +0000 (15:37 +0800)
* java/org/gnu/emacs/EmacsInputConnection.java
(EmacsInputConnection): Reimplement as an InputConnection, not
BaseInputConnection.
* src/androidterm.c (performEditorAction): Sync prior to sending
keyboard events.

java/org/gnu/emacs/EmacsInputConnection.java
src/androidterm.c

index 73c93c67ac72218733def96476d99223502e1928..1bcc9a62a813dd87be83c0ddfb17974c99277884 100644 (file)
@@ -19,26 +19,35 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
 package org.gnu.emacs;
 
-import android.view.inputmethod.BaseInputConnection;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.view.KeyEvent;
+
 import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
 import android.view.inputmethod.TextSnapshot;
 
-import android.view.KeyEvent;
-
-import android.os.Build;
-
 import android.util.Log;
 
 /* Android input methods, take number six.  See textconv.c for more
    details; this is more-or-less a thin wrapper around that file.  */
 
-public final class EmacsInputConnection extends BaseInputConnection
+public final class EmacsInputConnection implements InputConnection
 {
   private static final String TAG = "EmacsInputConnection";
+
+  /* View associated with this input connection.  */
   private EmacsView view;
+
+  /* The handle ID associated with that view's window.  */
   private short windowHandle;
 
   /* Whether or not to synchronize and call `updateIC' with the
@@ -77,15 +86,18 @@ public final class EmacsInputConnection extends BaseInputConnection
       extractAbsoluteOffsets = true;
   };
 
+
   public
   EmacsInputConnection (EmacsView view)
   {
-    super (view, true);
-
     this.view = view;
     this.windowHandle = view.window.handle;
   }
 
+\f
+  /* The functions below are called by input methods whenever they
+     need to perform an edit.  */
+
   @Override
   public boolean
   beginBatchEdit ()
@@ -116,7 +128,6 @@ public final class EmacsInputConnection extends BaseInputConnection
     return true;
   }
 
-  @Override
   public boolean
   commitCompletion (CompletionInfo info)
   {
@@ -133,6 +144,19 @@ public final class EmacsInputConnection extends BaseInputConnection
     return true;
   }
 
+  @Override
+  public boolean
+  commitCorrection (CorrectionInfo info)
+  {
+    /* The input method calls this function not to commit text, but to
+       indicate that a subsequent edit will consist of a correction.
+       Emacs has no use for this information.
+
+       Of course this completely contradicts the provided
+       documentation, but this is how Android actually behaves.  */
+    return false;
+  }
+
   @Override
   public boolean
   commitText (CharSequence text, int newCursorPosition)
@@ -170,6 +194,14 @@ public final class EmacsInputConnection extends BaseInputConnection
     return true;
   }
 
+  @Override
+  public boolean
+  commitText (CharSequence text, int newCursorPosition,
+             TextAttribute textAttribute)
+  {
+    return commitText (text, newCursorPosition);
+  }
+
   @Override
   public boolean
   deleteSurroundingText (int leftLength, int rightLength)
@@ -187,6 +219,16 @@ public final class EmacsInputConnection extends BaseInputConnection
     return true;
   }
 
+  @Override
+  public boolean
+  deleteSurroundingTextInCodePoints (int leftLength, int rightLength)
+  {
+    /* Emacs returns characters which cannot be represented in a Java
+       `char' as NULL characters, so code points always reflect
+       characters themselves.  */
+    return deleteSurroundingText (leftLength, rightLength);
+  }
+
   @Override
   public boolean
   finishComposingText ()
@@ -277,6 +319,14 @@ public final class EmacsInputConnection extends BaseInputConnection
     return true;
   }
 
+  @Override
+  public boolean
+  setComposingText (CharSequence text, int newCursorPosition,
+                   TextAttribute textAttribute)
+  {
+    return setComposingText (text, newCursorPosition);
+  }
+
   @Override
   public boolean
   setComposingRegion (int start, int end)
@@ -292,6 +342,13 @@ public final class EmacsInputConnection extends BaseInputConnection
     return true;
   }
 
+  @Override
+  public boolean
+  setComposingRegion (int start, int end, TextAttribute textAttribute)
+  {
+    return setComposingRegion (start, end);
+  }
+
   @Override
   public boolean
   performEditorAction (int editorAction)
@@ -430,6 +487,8 @@ public final class EmacsInputConnection extends BaseInputConnection
   }
 
   @Override
+  /* ACTION_MULTIPLE is apparently obsolete.  */
+  @SuppressWarnings ("deprecation")
   public boolean
   sendKeyEvent (KeyEvent key)
   {
@@ -440,20 +499,33 @@ public final class EmacsInputConnection extends BaseInputConnection
     if (EmacsService.DEBUG_IC)
       Log.d (TAG, "sendKeyEvent: " + key);
 
-    return super.sendKeyEvent (key);
-  }
+    /* Use the standard API if possible.  */
 
-  @Override
-  public boolean
-  deleteSurroundingTextInCodePoints (int beforeLength, int afterLength)
-  {
-    /* Return if the input connection is out of date.  */
-    if (view.icSerial < view.icGeneration)
-      return false;
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+      view.imManager.dispatchKeyEventFromInputMethod (view, key);
+    else
+      {
+       /* Fall back to dispatching the event manually if not.  */
+
+       switch (key.getAction ())
+         {
+         case KeyEvent.ACTION_DOWN:
+           view.onKeyDown (key.getKeyCode (), key);
+           break;
+
+         case KeyEvent.ACTION_UP:
+           view.onKeyUp (key.getKeyCode (), key);
+           break;
+
+         case KeyEvent.ACTION_MULTIPLE:
+           view.onKeyMultiple (key.getKeyCode (),
+                               key.getRepeatCount (),
+                               key);
+           break;
+         }
+      }
 
-    /* This can be implemented the same way as
-       deleteSurroundingText.  */
-    return this.deleteSurroundingText (beforeLength, afterLength);
+    return true;
   }
 
   @Override
@@ -471,6 +543,16 @@ public final class EmacsInputConnection extends BaseInputConnection
     return true;
   }
 
+  @Override
+  public boolean
+  requestCursorUpdates (int cursorUpdateMode, int filter)
+  {
+    if (filter != 0)
+      return false;
+
+    return requestCursorUpdates (cursorUpdateMode);
+  }
+
   @Override
   public SurroundingText
   getSurroundingText (int beforeLength, int afterLength,
@@ -505,11 +587,74 @@ public final class EmacsInputConnection extends BaseInputConnection
 \f
   /* Override functions which are not implemented.  */
 
+  @Override
+  public Handler
+  getHandler ()
+  {
+    return null;
+  }
+
+  @Override
+  public void
+  closeConnection ()
+  {
+
+  }
+
+  @Override
+  public boolean
+  commitContent (InputContentInfo inputContentInfo, int flags,
+                Bundle opts)
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  setImeConsumesInput (boolean imeConsumesInput)
+  {
+    return false;
+  }
+
   @Override
   public TextSnapshot
   takeSnapshot ()
   {
-    Log.d (TAG, "takeSnapshot");
     return null;
   }
+
+  @Override
+  public boolean
+  clearMetaKeyStates (int states)
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  reportFullscreenMode (boolean enabled)
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  performSpellCheck ()
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  performPrivateCommand (String action, Bundle data)
+  {
+    return false;
+  }
+
+  @Override
+  public int
+  getCursorCapsMode (int reqModes)
+  {
+    return 0;
+  }
 }
index f08536c02abeef68343a4d00c3cf99113214793c..191ff65199b3be7705ad74f5e81bb9f48b68131a 100644 (file)
@@ -5193,6 +5193,13 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object,
 
   union android_event event;
 
+  /* It's a good idea to call `android_sync_edit' before sending the
+     key event.  Otherwise, if RET causes the current window to be
+     changed, any text previously committed might end up in the newly
+     selected window.  */
+
+  android_sync_edit ();
+
   /* Undocumented behavior: performEditorAction is apparently expected
      to finish composing any text.  */