aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2023-06-15 12:36:50 +0800
committerPo Lu2023-06-15 12:36:50 +0800
commit363e293cc919ab02c40bd9a8fa4875c2e5644b2d (patch)
tree5fa537364e29fbcdb13cd195ac6da253a6d95771
parentca120044ac11d38ca1e8cac7903be38d5ca15d2b (diff)
downloademacs-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.java66
-rw-r--r--java/org/gnu/emacs/EmacsNative.java2
-rw-r--r--java/org/gnu/emacs/EmacsService.java25
-rw-r--r--java/org/gnu/emacs/EmacsView.java3
-rw-r--r--src/androidterm.c129
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;
26import android.view.inputmethod.ExtractedText; 26import android.view.inputmethod.ExtractedText;
27import android.view.inputmethod.ExtractedTextRequest; 27import android.view.inputmethod.ExtractedTextRequest;
28import android.view.inputmethod.SurroundingText; 28import android.view.inputmethod.SurroundingText;
29import android.view.inputmethod.TextSnapshot;
29 30
30public final class EmacsNative 31public 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
5711JNIEXPORT jobject JNICALL 5728/* Return a local reference to a `SurroundingText' object describing
5712NATIVE_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
5740static jobject
5741android_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
5832JNIEXPORT jobject JNICALL
5833NATIVE_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
5843JNIEXPORT jobject JNICALL
5844NATIVE_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