summaryrefslogtreecommitdiff
path: root/java/org/gnu/emacs/EmacsInputConnection.java
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2023-06-14 15:37:47 +0800
committerPo Lu <luangruo@yahoo.com>2023-06-14 15:37:47 +0800
commit90ae3cc387530229e5aca32c00d35495ab680e21 (patch)
treef35e1da961f97a04a9b35ff6e62536587a6164b6 /java/org/gnu/emacs/EmacsInputConnection.java
parent87b8f8769e6cd4563f82747c279c858617ce1b2b (diff)
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.
Diffstat (limited to 'java/org/gnu/emacs/EmacsInputConnection.java')
-rw-r--r--java/org/gnu/emacs/EmacsInputConnection.java189
1 files changed, 167 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)
{
@@ -135,6 +146,19 @@ public final class EmacsInputConnection extends BaseInputConnection
@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)
{
int[] selection;
@@ -172,6 +196,14 @@ public final class EmacsInputConnection extends BaseInputConnection
@Override
public boolean
+ commitText (CharSequence text, int newCursorPosition,
+ TextAttribute textAttribute)
+ {
+ return commitText (text, newCursorPosition);
+ }
+
+ @Override
+ public boolean
deleteSurroundingText (int leftLength, int rightLength)
{
/* Return if the input connection is out of date. */
@@ -189,6 +221,16 @@ public final class EmacsInputConnection extends BaseInputConnection
@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 ()
{
/* Return if the input connection is out of date. */
@@ -279,6 +321,14 @@ public final class EmacsInputConnection extends BaseInputConnection
@Override
public boolean
+ setComposingText (CharSequence text, int newCursorPosition,
+ TextAttribute textAttribute)
+ {
+ return setComposingText (text, newCursorPosition);
+ }
+
+ @Override
+ public boolean
setComposingRegion (int start, int end)
{
/* Return if the input connection is out of date. */
@@ -294,6 +344,13 @@ public final class EmacsInputConnection extends BaseInputConnection
@Override
public boolean
+ setComposingRegion (int start, int end, TextAttribute textAttribute)
+ {
+ return setComposingRegion (start, end);
+ }
+
+ @Override
+ public boolean
performEditorAction (int editorAction)
{
/* Return if the input connection is out of date. */
@@ -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
@@ -472,6 +544,16 @@ public final class EmacsInputConnection extends BaseInputConnection
}
@Override
+ public boolean
+ requestCursorUpdates (int cursorUpdateMode, int filter)
+ {
+ if (filter != 0)
+ return false;
+
+ return requestCursorUpdates (cursorUpdateMode);
+ }
+
+ @Override
public SurroundingText
getSurroundingText (int beforeLength, int afterLength,
int flags)
@@ -506,10 +588,73 @@ 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;
+ }
}