import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextSnapshot;
+
import android.view.KeyEvent;
import android.os.Build;
public boolean
beginBatchEdit ()
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "beginBatchEdit");
public boolean
endBatchEdit ()
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "endBatchEdit");
public boolean
commitCompletion (CompletionInfo info)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "commitCompletion: " + info);
{
int[] selection;
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
public boolean
deleteSurroundingText (int leftLength, int rightLength)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, ("deleteSurroundingText: "
+ leftLength + " " + rightLength));
public boolean
finishComposingText ()
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "finishComposingText");
public String
getSelectedText (int flags)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return null;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "getSelectedText: " + flags);
{
String string;
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return null;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
{
String string;
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return null;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
public boolean
setComposingText (CharSequence text, int newCursorPosition)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, ("setComposingText: "
+ text + " ## " + newCursorPosition));
public boolean
setComposingRegion (int start, int end)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "setComposingRegion: " + start + " " + end);
public boolean
performEditorAction (int editorAction)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "performEditorAction: " + editorAction);
{
int action;
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "performContextMenuAction: " + contextMenuAction);
ExtractedText text;
int[] selection;
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return null;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", "
+ request.hintMaxLines + " " + flags);
public boolean
setSelection (int start, int end)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "setSelection: " + start + " " + end);
public boolean
sendKeyEvent (KeyEvent key)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "sendKeyEvent: " + key);
public boolean
deleteSurroundingTextInCodePoints (int beforeLength, int afterLength)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
/* This can be implemented the same way as
deleteSurroundingText. */
return this.deleteSurroundingText (beforeLength, afterLength);
public boolean
requestCursorUpdates (int cursorUpdateMode)
{
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return false;
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode);
return true;
}
+ @Override
+ public SurroundingText
+ getSurroundingText (int beforeLength, int afterLength,
+ int flags)
+ {
+ SurroundingText text;
+
+ /* Return if the input connection is out of date. */
+ if (view.icSerial < view.icGeneration)
+ return null;
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, ("getSurroundingText: " + beforeLength + ", "
+ + afterLength));
+
+ text = EmacsNative.getSurroundingText (windowHandle, beforeLength,
+ afterLength, flags);
+
+ if (text != null)
+ Log.d (TAG, ("getSurroundingText: "
+ + text.getSelectionStart ()
+ + ","
+ + text.getSelectionEnd ()
+ + "+"
+ + text.getOffset ()
+ + ": "
+ + text.getText ()));
+
+ return text;
+ }
+
\f
/* Override functions which are not implemented. */
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.SurroundingText;
public final class EmacsNative
{
public static native void requestSelectionUpdate (short window);
public static native void requestCursorUpdates (short window, int mode);
public static native void clearInputFlags (short window);
+ public static native SurroundingText getSurroundingText (short window,
+ int left, int right,
+ int flags);
\f
/* Return the current value of the selection, or -1 upon
window.view.setICMode (icMode);
icBeginSynchronous ();
+ window.view.icGeneration++;
window.view.imManager.restartInput (window.view);
icEndSynchronous ();
}
details. */
private int icMode;
+ /* The number of calls to `resetIC' to have taken place the last
+ time an InputConnection was created. */
+ public long icSerial;
+
+ /* The number of calls to `recetIC' that have taken place. */
+ public volatile long icGeneration;
+
public
EmacsView (EmacsWindow window)
{
return null;
}
+ /* Set icSerial. If icSerial < icGeneration, the input connection
+ has been reset, and future input should be ignored until a new
+ connection is created. */
+
+ icSerial = icGeneration;
+
/* Reset flags set by the previous input method. */
EmacsNative.clearInputFlags (window.handle);
android_write_event (&event);
}
+\f
+
+/* Context for a call to `getSurroundingText'. */
+
+struct android_get_surrounding_text_context
+{
+ /* Number of characters before the region to return. */
+ int before_length;
+
+ /* Number of characters after the region to return. */
+ int after_length;
+
+ /* The returned text, or NULL. */
+ char *text;
+
+ /* The size of that text in characters and bytes. */
+ ptrdiff_t length, bytes;
+
+ /* Offsets into that text. */
+ ptrdiff_t offset, start, end;
+
+ /* The window. */
+ android_window window;
+};
+
+/* Return the surrounding text in the surrounding text context
+ specified by DATA. */
+
+static void
+android_get_surrounding_text (void *data)
+{
+ struct android_get_surrounding_text_context *request;
+ struct frame *f;
+ ptrdiff_t temp;
+
+ request = data;
+
+ /* Find the frame associated with the window. */
+ f = android_window_to_frame (NULL, request->window);
+
+ if (!f)
+ return;
+
+ /* Now get the surrounding text. */
+ request->text
+ = get_surrounding_text (f, request->before_length,
+ request->after_length, &request->length,
+ &request->bytes, &request->offset,
+ &request->start, &request->end);
+
+ /* Sort request->start and request->end for compatibility with some
+ bad input methods. */
+
+ if (request->end < request->start)
+ {
+ temp = request->start;
+ request->start = request->end;
+ request->end = temp;
+ }
+}
+
+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;
+
+ static jclass class;
+ static jmethodID constructor;
+
+ struct android_get_surrounding_text_context context;
+ jstring string;
+ jobject object;
+
+ /* Initialize CLASS if it has not yet been initialized. */
+
+ if (!class)
+ {
+ class
+ = (*env)->FindClass (env, ("android/view/inputmethod"
+ "/SurroundingText"));
+
+#if __ANDROID_API__ < 31
+ /* If CLASS cannot be found, the version of Android currently
+ running is too old. */
+
+ if (!class)
+ {
+ (*env)->ExceptionClear (env);
+ return NULL;
+ }
+#else /* __ANDROID_API__ >= 31 */
+ assert (class);
+#endif /* __ANDROID_API__ < 31 */
+
+ class = (*env)->NewGlobalRef (env, class);
+ if (!class)
+ return NULL;
+
+ /* Now look for its constructor. */
+ constructor = (*env)->GetMethodID (env, class, "<init>",
+ "(Ljava/lang/CharSequence;III)V");
+ assert (constructor);
+ }
+
+ context.before_length = before_length;
+ context.after_length = after_length;
+ context.window = window;
+ context.text = NULL;
+
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_get_surrounding_text,
+ &context))
+ return NULL;
+
+ if (!context.text)
+ return NULL;
+
+ /* Encode the returned text. */
+ string = android_text_to_string (env, context.text, context.length,
+ context.bytes);
+ free (context.text);
+
+ if (!string)
+ return NULL;
+
+ /* Create an SurroundingText object containing this information. */
+ object = (*env)->NewObject (env, class, constructor, string,
+ (jint) min (context.start,
+ TYPE_MAXIMUM (jint)),
+ (jint) min (context.end,
+ TYPE_MAXIMUM (jint)),
+ /* Adjust point offsets to fit into
+ Android's 0-based indexing. */
+ (jint) min (context.offset - 1,
+ TYPE_MAXIMUM (jint)));
+ if (!object)
+ return NULL;
+
+ return object;
+}
+
#ifdef __clang__
#pragma clang diagnostic pop
#else
return buffer;
}
+/* Return the text between the positions PT - LEFT and PT + RIGHT. If
+ the mark is active, return the range of text relative to the bounds
+ of the region instead.
+
+ Set *LENGTH to the number of characters returned, *BYTES to the
+ number of bytes returned, *OFFSET to the character position of the
+ returned text, and *START_RETURN and *END_RETURN to the mark and
+ point relative to that position. */
+
+char *
+get_surrounding_text (struct frame *f, ptrdiff_t left,
+ ptrdiff_t right, ptrdiff_t *length,
+ ptrdiff_t *bytes, ptrdiff_t *offset,
+ ptrdiff_t *start_return,
+ ptrdiff_t *end_return)
+{
+ specpdl_ref count;
+ ptrdiff_t start, end, start_byte, end_byte, mark, temp;
+ char *buffer;
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return NULL;
+
+ /* Save the excursion, as there will be extensive changes to the
+ selected window. */
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_excursion ();
+
+ /* Inhibit quitting. */
+ specbind (Qinhibit_quit, Qt);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+ buffer = NULL;
+
+ /* Figure out the bounds of the text to return. */
+
+ /* First, obtain start and end. */
+ end = get_mark ();
+ start = PT;
+
+ /* If the mark is not active, make it start and end. */
+
+ if (end == -1)
+ end = start;
+
+ /* Now sort start and end. */
+
+ if (end < start)
+ {
+ temp = start;
+ start = end;
+ end = temp;
+ }
+
+ /* And subtract left and right. */
+
+ if (INT_SUBTRACT_WRAPV (start, left, &start)
+ || INT_ADD_WRAPV (end, right, &end))
+ goto finish;
+
+ start = max (start, BEGV);
+ end = min (end, ZV);
+
+ /* Detect overflow. */
+
+ if (!(start <= PT && PT <= end))
+ goto finish;
+
+ /* Convert the character positions to byte positions. */
+ start_byte = CHAR_TO_BYTE (start);
+ end_byte = CHAR_TO_BYTE (end);
+
+ /* Extract the text from the buffer. */
+ buffer = xmalloc (end_byte - start_byte);
+ copy_buffer (start, start_byte, end, end_byte,
+ buffer);
+
+ /* Get the mark. If it's not active, use PT. */
+
+ mark = get_mark ();
+
+ if (mark == -1)
+ mark = PT;
+
+ /* Return the offsets. Unlike `get_extracted_text', this need not
+ sort mark and point. */
+
+ *offset = start;
+ *start_return = mark - start;
+ *end_return = PT - start;
+ *length = end - start;
+ *bytes = end_byte - start_byte;
+
+ finish:
+ unbind_to (count, Qnil);
+ return buffer;
+}
+
/* Return whether or not text conversion is temporarily disabled.
`reset' should always call this to determine whether or not to
disable the input method. */
extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
ptrdiff_t *, ptrdiff_t *, ptrdiff_t *,
ptrdiff_t *, bool *);
+extern char *get_surrounding_text (struct frame *, ptrdiff_t,
+ ptrdiff_t, ptrdiff_t *,
+ ptrdiff_t *, ptrdiff_t *,
+ ptrdiff_t *, ptrdiff_t *);
extern bool conversion_disabled_p (void);
extern void register_textconv_interface (struct textconv_interface *);