diff options
| author | Po Lu | 2023-06-15 12:36:50 +0800 |
|---|---|---|
| committer | Po Lu | 2023-06-15 12:36:50 +0800 |
| commit | 363e293cc919ab02c40bd9a8fa4875c2e5644b2d (patch) | |
| tree | 5fa537364e29fbcdb13cd195ac6da253a6d95771 | |
| parent | ca120044ac11d38ca1e8cac7903be38d5ca15d2b (diff) | |
| download | emacs-363e293cc919ab02c40bd9a8fa4875c2e5644b2d.tar.gz emacs-363e293cc919ab02c40bd9a8fa4875c2e5644b2d.zip | |
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.
| -rw-r--r-- | java/org/gnu/emacs/EmacsInputConnection.java | 66 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsNative.java | 2 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsService.java | 25 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsView.java | 3 | ||||
| -rw-r--r-- | 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 | |||
| 50 | /* The handle ID associated with that view's window. */ | 50 | /* The handle ID associated with that view's window. */ |
| 51 | private short windowHandle; | 51 | private short windowHandle; |
| 52 | 52 | ||
| 53 | /* Number of batch edits currently underway. Used to avoid | ||
| 54 | synchronizing with the Emacs thread after each | ||
| 55 | `endBatchEdit'. */ | ||
| 56 | private int batchEditCount; | ||
| 57 | |||
| 53 | /* Whether or not to synchronize and call `updateIC' with the | 58 | /* Whether or not to synchronize and call `updateIC' with the |
| 54 | selection position after committing text. | 59 | selection position after committing text. |
| 55 | 60 | ||
| @@ -110,6 +115,10 @@ public final class EmacsInputConnection implements InputConnection | |||
| 110 | Log.d (TAG, "beginBatchEdit"); | 115 | Log.d (TAG, "beginBatchEdit"); |
| 111 | 116 | ||
| 112 | EmacsNative.beginBatchEdit (windowHandle); | 117 | EmacsNative.beginBatchEdit (windowHandle); |
| 118 | |||
| 119 | /* Keep a record of the number of outstanding batch edits here as | ||
| 120 | well. */ | ||
| 121 | batchEditCount++; | ||
| 113 | return true; | 122 | return true; |
| 114 | } | 123 | } |
| 115 | 124 | ||
| @@ -125,7 +134,14 @@ public final class EmacsInputConnection implements InputConnection | |||
| 125 | Log.d (TAG, "endBatchEdit"); | 134 | Log.d (TAG, "endBatchEdit"); |
| 126 | 135 | ||
| 127 | EmacsNative.endBatchEdit (windowHandle); | 136 | EmacsNative.endBatchEdit (windowHandle); |
| 128 | return true; | 137 | |
| 138 | /* Subtract one from the UI thread record of the number of batch | ||
| 139 | edits currently under way. */ | ||
| 140 | |||
| 141 | if (batchEditCount > 0) | ||
| 142 | batchEditCount -= 1; | ||
| 143 | |||
| 144 | return batchEditCount > 0; | ||
| 129 | } | 145 | } |
| 130 | 146 | ||
| 131 | public boolean | 147 | public boolean |
| @@ -584,21 +600,50 @@ public final class EmacsInputConnection implements InputConnection | |||
| 584 | return text; | 600 | return text; |
| 585 | } | 601 | } |
| 586 | 602 | ||
| 587 | |||
| 588 | /* Override functions which are not implemented. */ | ||
| 589 | |||
| 590 | @Override | 603 | @Override |
| 591 | public Handler | 604 | public TextSnapshot |
| 592 | getHandler () | 605 | takeSnapshot () |
| 593 | { | 606 | { |
| 594 | return null; | 607 | TextSnapshot snapshot; |
| 608 | |||
| 609 | /* Return if the input connection is out of date. */ | ||
| 610 | if (view.icSerial < view.icGeneration) | ||
| 611 | return null; | ||
| 612 | |||
| 613 | snapshot = EmacsNative.takeSnapshot (windowHandle); | ||
| 614 | |||
| 615 | if (EmacsService.DEBUG_IC) | ||
| 616 | Log.d (TAG, ("takeSnapshot: " | ||
| 617 | + snapshot.getSurroundingText ().getText () | ||
| 618 | + " @ " + snapshot.getCompositionEnd () | ||
| 619 | + ", " + snapshot.getCompositionStart ())); | ||
| 620 | |||
| 621 | return snapshot; | ||
| 595 | } | 622 | } |
| 596 | 623 | ||
| 597 | @Override | 624 | @Override |
| 598 | public void | 625 | public void |
| 599 | closeConnection () | 626 | closeConnection () |
| 600 | { | 627 | { |
| 628 | batchEditCount = 0; | ||
| 629 | } | ||
| 630 | |||
| 631 | |||
| 601 | 632 | ||
| 633 | public void | ||
| 634 | reset () | ||
| 635 | { | ||
| 636 | batchEditCount = 0; | ||
| 637 | } | ||
| 638 | |||
| 639 | |||
| 640 | /* Override functions which are not implemented. */ | ||
| 641 | |||
| 642 | @Override | ||
| 643 | public Handler | ||
| 644 | getHandler () | ||
| 645 | { | ||
| 646 | return null; | ||
| 602 | } | 647 | } |
| 603 | 648 | ||
| 604 | @Override | 649 | @Override |
| @@ -617,13 +662,6 @@ public final class EmacsInputConnection implements InputConnection | |||
| 617 | } | 662 | } |
| 618 | 663 | ||
| 619 | @Override | 664 | @Override |
| 620 | public TextSnapshot | ||
| 621 | takeSnapshot () | ||
| 622 | { | ||
| 623 | return null; | ||
| 624 | } | ||
| 625 | |||
| 626 | @Override | ||
| 627 | public boolean | 665 | public boolean |
| 628 | clearMetaKeyStates (int states) | 666 | clearMetaKeyStates (int states) |
| 629 | { | 667 | { |
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; | |||
| 26 | import android.view.inputmethod.ExtractedText; | 26 | import android.view.inputmethod.ExtractedText; |
| 27 | import android.view.inputmethod.ExtractedTextRequest; | 27 | import android.view.inputmethod.ExtractedTextRequest; |
| 28 | import android.view.inputmethod.SurroundingText; | 28 | import android.view.inputmethod.SurroundingText; |
| 29 | import android.view.inputmethod.TextSnapshot; | ||
| 29 | 30 | ||
| 30 | public final class EmacsNative | 31 | public final class EmacsNative |
| 31 | { | 32 | { |
| @@ -230,6 +231,7 @@ public final class EmacsNative | |||
| 230 | public static native SurroundingText getSurroundingText (short window, | 231 | public static native SurroundingText getSurroundingText (short window, |
| 231 | int left, int right, | 232 | int left, int right, |
| 232 | int flags); | 233 | int flags); |
| 234 | public static native TextSnapshot takeSnapshot (short window); | ||
| 233 | 235 | ||
| 234 | 236 | ||
| 235 | /* Return the current value of the selection, or -1 upon | 237 | /* 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 | |||
| 767 | public void | 767 | public void |
| 768 | resetIC (EmacsWindow window, int icMode) | 768 | resetIC (EmacsWindow window, int icMode) |
| 769 | { | 769 | { |
| 770 | int oldMode; | ||
| 771 | |||
| 770 | if (DEBUG_IC) | 772 | if (DEBUG_IC) |
| 771 | Log.d (TAG, "resetIC: " + window); | 773 | Log.d (TAG, "resetIC: " + window + ", " + icMode); |
| 774 | |||
| 775 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU | ||
| 776 | && (oldMode = window.view.getICMode ()) == icMode | ||
| 777 | /* Don't do this if there is currently no input | ||
| 778 | connection. */ | ||
| 779 | && oldMode != IC_MODE_NULL) | ||
| 780 | { | ||
| 781 | if (DEBUG_IC) | ||
| 782 | Log.d (TAG, "resetIC: calling invalidateInput"); | ||
| 783 | |||
| 784 | /* Android 33 and later allow the IM reset to be optimized out | ||
| 785 | and replaced by a call to `invalidateInput', which is much | ||
| 786 | faster, as it does not involve resetting the input | ||
| 787 | connection. */ | ||
| 788 | |||
| 789 | icBeginSynchronous (); | ||
| 790 | window.view.imManager.invalidateInput (window.view); | ||
| 791 | icEndSynchronous (); | ||
| 792 | |||
| 793 | return; | ||
| 794 | } | ||
| 772 | 795 | ||
| 773 | window.view.setICMode (icMode); | 796 | window.view.setICMode (icMode); |
| 774 | 797 | ||
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 | |||
| 681 | 681 | ||
| 682 | if (inputConnection == null) | 682 | if (inputConnection == null) |
| 683 | inputConnection = new EmacsInputConnection (this); | 683 | inputConnection = new EmacsInputConnection (this); |
| 684 | else | ||
| 685 | /* Clear several pieces of state in the input connection. */ | ||
| 686 | inputConnection.reset (); | ||
| 684 | 687 | ||
| 685 | /* Return the input connection. */ | 688 | /* Return the input connection. */ |
| 686 | return inputConnection; | 689 | 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 | |||
| 5668 | /* Offsets into that text. */ | 5668 | /* Offsets into that text. */ |
| 5669 | ptrdiff_t offset, start, end; | 5669 | ptrdiff_t offset, start, end; |
| 5670 | 5670 | ||
| 5671 | /* The start and end indices of the conversion region. | ||
| 5672 | -1 if it does not exist. */ | ||
| 5673 | ptrdiff_t conversion_start, conversion_end; | ||
| 5674 | |||
| 5671 | /* The window. */ | 5675 | /* The window. */ |
| 5672 | android_window window; | 5676 | android_window window; |
| 5673 | }; | 5677 | }; |
| @@ -5706,22 +5710,47 @@ android_get_surrounding_text (void *data) | |||
| 5706 | request->start = request->end; | 5710 | request->start = request->end; |
| 5707 | request->end = temp; | 5711 | request->end = temp; |
| 5708 | } | 5712 | } |
| 5713 | |||
| 5714 | /* Retrieve the conversion region. */ | ||
| 5715 | |||
| 5716 | request->conversion_start = -1; | ||
| 5717 | request->conversion_end = -1; | ||
| 5718 | |||
| 5719 | if (MARKERP (f->conversion.compose_region_start)) | ||
| 5720 | { | ||
| 5721 | request->conversion_start | ||
| 5722 | = marker_position (f->conversion.compose_region_start) - 1; | ||
| 5723 | request->conversion_end | ||
| 5724 | = marker_position (f->conversion.compose_region_end) - 1; | ||
| 5725 | } | ||
| 5709 | } | 5726 | } |
| 5710 | 5727 | ||
| 5711 | JNIEXPORT jobject JNICALL | 5728 | /* Return a local reference to a `SurroundingText' object describing |
| 5712 | NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object, | 5729 | WINDOW's surrounding text. ENV should be a valid JNI environment |
| 5713 | jshort window, jint before_length, | 5730 | for the current thread. |
| 5714 | jint after_length, jint flags) | ||
| 5715 | { | ||
| 5716 | JNI_STACK_ALIGNMENT_PROLOGUE; | ||
| 5717 | 5731 | ||
| 5718 | static jclass class; | 5732 | BEFORE_LENGTH and AFTER_LENGTH specify the number of characters |
| 5719 | static jmethodID constructor; | 5733 | around point and mark to return. |
| 5720 | 5734 | ||
| 5735 | Return the conversion region (or -1) in *CONVERSION_START and | ||
| 5736 | *CONVERSION_END if non-NULL. | ||
| 5737 | |||
| 5738 | Value is the object upon success, else NULL. */ | ||
| 5739 | |||
| 5740 | static jobject | ||
| 5741 | android_get_surrounding_text_internal (JNIEnv *env, jshort window, | ||
| 5742 | jint before_length, | ||
| 5743 | jint after_length, | ||
| 5744 | ptrdiff_t *conversion_start, | ||
| 5745 | ptrdiff_t *conversion_end) | ||
| 5746 | { | ||
| 5721 | struct android_get_surrounding_text_context context; | 5747 | struct android_get_surrounding_text_context context; |
| 5722 | jstring string; | 5748 | jstring string; |
| 5723 | jobject object; | 5749 | jobject object; |
| 5724 | 5750 | ||
| 5751 | static jclass class; | ||
| 5752 | static jmethodID constructor; | ||
| 5753 | |||
| 5725 | /* Initialize CLASS if it has not yet been initialized. */ | 5754 | /* Initialize CLASS if it has not yet been initialized. */ |
| 5726 | 5755 | ||
| 5727 | if (!class) | 5756 | if (!class) |
| @@ -5745,7 +5774,9 @@ NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object, | |||
| 5745 | 5774 | ||
| 5746 | class = (*env)->NewGlobalRef (env, class); | 5775 | class = (*env)->NewGlobalRef (env, class); |
| 5747 | if (!class) | 5776 | if (!class) |
| 5748 | return NULL; | 5777 | /* Clear class to prevent a local reference from remaining in |
| 5778 | `class'. */ | ||
| 5779 | return (class = NULL); | ||
| 5749 | 5780 | ||
| 5750 | /* Now look for its constructor. */ | 5781 | /* Now look for its constructor. */ |
| 5751 | constructor = (*env)->GetMethodID (env, class, "<init>", | 5782 | constructor = (*env)->GetMethodID (env, class, "<init>", |
| @@ -5787,6 +5818,86 @@ NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object, | |||
| 5787 | if (!object) | 5818 | if (!object) |
| 5788 | return NULL; | 5819 | return NULL; |
| 5789 | 5820 | ||
| 5821 | /* Now return the conversion region if that was requested. */ | ||
| 5822 | |||
| 5823 | if (conversion_start) | ||
| 5824 | { | ||
| 5825 | *conversion_start = context.conversion_start; | ||
| 5826 | *conversion_end = context.conversion_start; | ||
| 5827 | } | ||
| 5828 | |||
| 5829 | return object; | ||
| 5830 | } | ||
| 5831 | |||
| 5832 | JNIEXPORT jobject JNICALL | ||
| 5833 | NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject object, | ||
| 5834 | jshort window, jint before_length, | ||
| 5835 | jint after_length, jint flags) | ||
| 5836 | { | ||
| 5837 | JNI_STACK_ALIGNMENT_PROLOGUE; | ||
| 5838 | |||
| 5839 | return android_get_surrounding_text_internal (env, window, before_length, | ||
| 5840 | after_length, NULL, NULL); | ||
| 5841 | } | ||
| 5842 | |||
| 5843 | JNIEXPORT jobject JNICALL | ||
| 5844 | NATIVE_NAME (takeSnapshot) (JNIEnv *env, jobject object, jshort window) | ||
| 5845 | { | ||
| 5846 | JNI_STACK_ALIGNMENT_PROLOGUE; | ||
| 5847 | |||
| 5848 | jobject text; | ||
| 5849 | ptrdiff_t start, end; | ||
| 5850 | |||
| 5851 | static jclass class; | ||
| 5852 | static jmethodID constructor; | ||
| 5853 | |||
| 5854 | /* First, obtain the surrounding text and conversion region. */ | ||
| 5855 | text = android_get_surrounding_text_internal (env, window, 600, 600, | ||
| 5856 | &start, &end); | ||
| 5857 | |||
| 5858 | /* If that fails, return NULL. */ | ||
| 5859 | |||
| 5860 | if (!text) | ||
| 5861 | return NULL; | ||
| 5862 | |||
| 5863 | /* Next, initialize the TextSnapshot class. */ | ||
| 5864 | |||
| 5865 | if (!class) | ||
| 5866 | { | ||
| 5867 | class | ||
| 5868 | = (*env)->FindClass (env, ("android/view/inputmethod" | ||
| 5869 | "/TextSnapshot")); | ||
| 5870 | #if __ANDROID_API__ < 33 | ||
| 5871 | /* If CLASS cannot be found, the version of Android currently | ||
| 5872 | running is too old. */ | ||
| 5873 | |||
| 5874 | if (!class) | ||
| 5875 | { | ||
| 5876 | (*env)->ExceptionClear (env); | ||
| 5877 | return NULL; | ||
| 5878 | } | ||
| 5879 | #else /* __ANDROID_API__ >= 33 */ | ||
| 5880 | assert (class); | ||
| 5881 | #endif /* __ANDROID_API__ < 33 */ | ||
| 5882 | |||
| 5883 | class = (*env)->NewGlobalRef (env, class); | ||
| 5884 | if (!class) | ||
| 5885 | /* Clear class to prevent a local reference from remaining in | ||
| 5886 | `class'. */ | ||
| 5887 | return (class = NULL); | ||
| 5888 | |||
| 5889 | constructor = (*env)->GetMethodID (env, class, "<init>", | ||
| 5890 | "(Landroid/view/inputmethod" | ||
| 5891 | "/SurroundingText;III)V"); | ||
| 5892 | assert (constructor); | ||
| 5893 | } | ||
| 5894 | |||
| 5895 | /* Try to create a TextSnapshot object. */ | ||
| 5896 | eassert (start <= end); | ||
| 5897 | object = (*env)->NewObject (env, class, constructor, text, | ||
| 5898 | (jint) min (start, TYPE_MAXIMUM (jint)), | ||
| 5899 | (jint) min (end, TYPE_MAXIMUM (jint)), | ||
| 5900 | (jint) 0); | ||
| 5790 | return object; | 5901 | return object; |
| 5791 | } | 5902 | } |
| 5792 | 5903 | ||