[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/android 90ae3cc3875 2/2: Improve IM synchronization on Android
From: |
Po Lu |
Subject: |
feature/android 90ae3cc3875 2/2: Improve IM synchronization on Android |
Date: |
Wed, 14 Jun 2023 03:38:44 -0400 (EDT) |
branch: feature/android
commit 90ae3cc387530229e5aca32c00d35495ab680e21
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Improve IM synchronization on Android
* 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 | 189 +++++++++++++++++++++++----
src/androidterm.c | 7 +
2 files changed, 174 insertions(+), 22 deletions(-)
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java
b/java/org/gnu/emacs/EmacsInputConnection.java
index 73c93c67ac7..1bcc9a62a81 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -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;
}
+
+ /* 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
/* 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;
+ }
}
diff --git a/src/androidterm.c b/src/androidterm.c
index f08536c02ab..191ff65199b 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -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. */