aboutsummaryrefslogtreecommitdiffstats
path: root/java/org/gnu
diff options
context:
space:
mode:
authorPo Lu2023-02-12 20:32:25 +0800
committerPo Lu2023-02-12 20:32:25 +0800
commit0198b8cffd82893412c738dae8e50c45a99286f1 (patch)
tree5dd87dc03d1c1ee08205c988f2c287f714f1cfa3 /java/org/gnu
parentab48881a2fb72df016f2a00bc107e5a35a411a9d (diff)
downloademacs-0198b8cffd82893412c738dae8e50c45a99286f1.tar.gz
emacs-0198b8cffd82893412c738dae8e50c45a99286f1.zip
Update Android port
* doc/emacs/android.texi (Android Environment): Document notifications permission. * java/org/gnu/emacs/EmacsEditable.java (EmacsEditable): * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): New files. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Load library dependencies in a less verbose fashion. * java/org/gnu/emacs/EmacsView.java (EmacsView): Make imManager public. (onCreateInputConnection): Set InputType to TYPE_NULL for now. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyDown) (onKeyUp, getEventUnicodeChar): Correctly handle key events with strings. * lisp/term/android-win.el (android-clear-preedit-text) (android-preedit-text): New special event handlers. * src/android.c (struct android_emacs_window): Add function lookup_string. (android_init_emacs_window): Adjust accordingly. (android_wc_lookup_string): New function. * src/androidgui.h (struct android_key_event): Improve commentary. (enum android_lookup_status): New enum. * src/androidterm.c (handle_one_android_event): Synchronize IM lookup code with X. * src/coding.c (from_unicode_buffer): Implement on Android. * src/coding.h: * src/sfnt.c: Fix commentary.
Diffstat (limited to 'java/org/gnu')
-rw-r--r--java/org/gnu/emacs/EmacsEditable.java300
-rw-r--r--java/org/gnu/emacs/EmacsInputConnection.java175
-rw-r--r--java/org/gnu/emacs/EmacsNative.java166
-rw-r--r--java/org/gnu/emacs/EmacsView.java7
-rw-r--r--java/org/gnu/emacs/EmacsWindow.java96
5 files changed, 588 insertions, 156 deletions
diff --git a/java/org/gnu/emacs/EmacsEditable.java b/java/org/gnu/emacs/EmacsEditable.java
new file mode 100644
index 00000000000..79af65a6ccd
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsEditable.java
@@ -0,0 +1,300 @@
1/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20package org.gnu.emacs;
21
22import android.text.InputFilter;
23import android.text.SpannableStringBuilder;
24import android.text.Spanned;
25import android.text.SpanWatcher;
26import android.text.Selection;
27
28import android.content.Context;
29
30import android.view.inputmethod.InputMethodManager;
31import android.view.inputmethod.ExtractedText;
32import android.view.inputmethod.ExtractedTextRequest;
33
34import android.text.Spannable;
35
36import android.util.Log;
37
38import android.os.Build;
39
40/* Android input methods insist on having access to buffer contents.
41 Since Emacs is not designed like ``any other Android text editor'',
42 that is not possible.
43
44 This file provides a fake editing buffer that is designed to weasel
45 as much information as possible out of an input method, without
46 actually providing buffer contents to Emacs.
47
48 The basic idea is to have the fake editing buffer be initially
49 empty.
50
51 When the input method inserts composed text, it sets a flag.
52 Updates to the buffer while the flag is set are sent to Emacs to be
53 displayed as ``preedit text''.
54
55 Once some heuristics decide that composition has been completed,
56 the composed text is sent to Emacs, and the text that was inserted
57 in this editing buffer is erased. */
58
59public class EmacsEditable extends SpannableStringBuilder
60 implements SpanWatcher
61{
62 private static final String TAG = "EmacsEditable";
63
64 /* Whether or not composition is currently in progress. */
65 private boolean isComposing;
66
67 /* The associated input connection. */
68 private EmacsInputConnection connection;
69
70 /* The associated IM manager. */
71 private InputMethodManager imManager;
72
73 /* Any extracted text an input method may be monitoring. */
74 private ExtractedText extractedText;
75
76 /* The corresponding text request. */
77 private ExtractedTextRequest extractRequest;
78
79 /* The number of nested batch edits. */
80 private int batchEditCount;
81
82 /* Whether or not invalidateInput should be called upon batch edits
83 ending. */
84 private boolean pendingInvalidate;
85
86 /* The ``composing span'' indicating the bounds of an ongoing
87 character composition. */
88 private Object composingSpan;
89
90 public
91 EmacsEditable (EmacsInputConnection connection)
92 {
93 /* Initialize the editable with one initial space, so backspace
94 always works. */
95 super ();
96
97 Object tem;
98 Context context;
99
100 this.connection = connection;
101
102 context = connection.view.getContext ();
103 tem = context.getSystemService (Context.INPUT_METHOD_SERVICE);
104 imManager = (InputMethodManager) tem;
105
106 /* To watch for changes to text properties on Android, you
107 add... a text property. */
108 setSpan (this, 0, 0, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
109 }
110
111 public void
112 endBatchEdit ()
113 {
114 if (batchEditCount < 1)
115 return;
116
117 if (--batchEditCount == 0 && pendingInvalidate)
118 invalidateInput ();
119 }
120
121 public void
122 beginBatchEdit ()
123 {
124 ++batchEditCount;
125 }
126
127 public void
128 setExtractedTextAndRequest (ExtractedText text,
129 ExtractedTextRequest request,
130 boolean monitor)
131 {
132 /* Extract the text. If monitor is set, also record it as the
133 text that is currently being extracted. */
134
135 text.startOffset = 0;
136 text.selectionStart = Selection.getSelectionStart (this);
137 text.selectionEnd = Selection.getSelectionStart (this);
138 text.text = this;
139
140 if (monitor)
141 {
142 extractedText = text;
143 extractRequest = request;
144 }
145 }
146
147 public void
148 compositionStart ()
149 {
150 isComposing = true;
151 }
152
153 public void
154 compositionEnd ()
155 {
156 isComposing = false;
157 sendComposingText (null);
158 }
159
160 private void
161 sendComposingText (String string)
162 {
163 EmacsWindow window;
164 long time, serial;
165
166 window = connection.view.window;
167
168 if (window.isDestroyed ())
169 return;
170
171 time = System.currentTimeMillis ();
172
173 /* A composition event is simply a special key event with a
174 keycode of -1. */
175
176 synchronized (window.eventStrings)
177 {
178 serial
179 = EmacsNative.sendKeyPress (window.handle, time, 0, -1, -1);
180
181 /* Save the string so that android_lookup_string can find
182 it. */
183 if (string != null)
184 window.saveUnicodeString ((int) serial, string);
185 }
186 }
187
188 private void
189 invalidateInput ()
190 {
191 int start, end, composingSpanStart, composingSpanEnd;
192
193 if (batchEditCount > 0)
194 {
195 Log.d (TAG, "invalidateInput: deferring for batch edit");
196 pendingInvalidate = true;
197 return;
198 }
199
200 pendingInvalidate = false;
201
202 start = Selection.getSelectionStart (this);
203 end = Selection.getSelectionEnd (this);
204
205 if (composingSpan != null)
206 {
207 composingSpanStart = getSpanStart (composingSpan);
208 composingSpanEnd = getSpanEnd (composingSpan);
209 }
210 else
211 {
212 composingSpanStart = -1;
213 composingSpanEnd = -1;
214 }
215
216 Log.d (TAG, "invalidateInput: now " + start + ", " + end);
217
218 /* Tell the input method that the cursor changed. */
219 imManager.updateSelection (connection.view, start, end,
220 composingSpanStart,
221 composingSpanEnd);
222
223 /* If there is any extracted text, tell the IME that it has
224 changed. */
225 if (extractedText != null)
226 imManager.updateExtractedText (connection.view,
227 extractRequest.token,
228 extractedText);
229 }
230
231 public SpannableStringBuilder
232 replace (int start, int end, CharSequence tb, int tbstart,
233 int tbend)
234 {
235 super.replace (start, end, tb, tbstart, tbend);
236
237 /* If a change happens during composition, perform the change and
238 then send the text being composed. */
239
240 if (isComposing)
241 sendComposingText (toString ());
242
243 return this;
244 }
245
246 private boolean
247 isSelectionSpan (Object span)
248 {
249 return ((Selection.SELECTION_START == span
250 || Selection.SELECTION_END == span)
251 && (getSpanFlags (span)
252 & Spanned.SPAN_INTERMEDIATE) == 0);
253 }
254
255 @Override
256 public void
257 onSpanAdded (Spannable text, Object what, int start, int end)
258 {
259 Log.d (TAG, "onSpanAdded: " + text + " " + what + " "
260 + start + " " + end);
261
262 /* Try to find the composing span. This isn't a public API. */
263
264 if (what.getClass ().getName ().contains ("ComposingText"))
265 composingSpan = what;
266
267 if (isSelectionSpan (what))
268 invalidateInput ();
269 }
270
271 @Override
272 public void
273 onSpanChanged (Spannable text, Object what, int ostart,
274 int oend, int nstart, int nend)
275 {
276 Log.d (TAG, "onSpanChanged: " + text + " " + what + " "
277 + nstart + " " + nend);
278
279 if (isSelectionSpan (what))
280 invalidateInput ();
281 }
282
283 @Override
284 public void
285 onSpanRemoved (Spannable text, Object what,
286 int start, int end)
287 {
288 Log.d (TAG, "onSpanRemoved: " + text + " " + what + " "
289 + start + " " + end);
290
291 if (isSelectionSpan (what))
292 invalidateInput ();
293 }
294
295 public boolean
296 isInBatchEdit ()
297 {
298 return batchEditCount > 0;
299 }
300}
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java
new file mode 100644
index 00000000000..897a393b984
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -0,0 +1,175 @@
1/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20package org.gnu.emacs;
21
22import android.view.inputmethod.BaseInputConnection;
23import android.view.inputmethod.CompletionInfo;
24import android.view.inputmethod.ExtractedText;
25import android.view.inputmethod.ExtractedTextRequest;
26import android.view.inputmethod.InputMethodManager;
27import android.view.inputmethod.SurroundingText;
28import android.view.KeyEvent;
29
30import android.text.Editable;
31
32import android.util.Log;
33
34/* Android input methods, take number six.
35
36 See EmacsEditable for more details. */
37
38public class EmacsInputConnection extends BaseInputConnection
39{
40 private static final String TAG = "EmacsInputConnection";
41 public EmacsView view;
42 private EmacsEditable editable;
43
44 /* The length of the last string to be committed. */
45 private int lastCommitLength;
46
47 int currentLargeOffset;
48
49 public
50 EmacsInputConnection (EmacsView view)
51 {
52 super (view, false);
53 this.view = view;
54 this.editable = new EmacsEditable (this);
55 }
56
57 @Override
58 public Editable
59 getEditable ()
60 {
61 return editable;
62 }
63
64 @Override
65 public boolean
66 setComposingText (CharSequence text, int newCursorPosition)
67 {
68 editable.compositionStart ();
69 super.setComposingText (text, newCursorPosition);
70 return true;
71 }
72
73 @Override
74 public boolean
75 setComposingRegion (int start, int end)
76 {
77 int i;
78
79 if (lastCommitLength != 0)
80 {
81 Log.d (TAG, "Restarting composition for: " + lastCommitLength);
82
83 for (i = 0; i < lastCommitLength; ++i)
84 sendKeyEvent (new KeyEvent (KeyEvent.ACTION_DOWN,
85 KeyEvent.KEYCODE_DEL));
86
87 lastCommitLength = 0;
88 }
89
90 editable.compositionStart ();
91 super.setComposingRegion (start, end);
92 return true;
93 }
94
95 @Override
96 public boolean
97 finishComposingText ()
98 {
99 editable.compositionEnd ();
100 return super.finishComposingText ();
101 }
102
103 @Override
104 public boolean
105 beginBatchEdit ()
106 {
107 editable.beginBatchEdit ();
108 return super.beginBatchEdit ();
109 }
110
111 @Override
112 public boolean
113 endBatchEdit ()
114 {
115 editable.endBatchEdit ();
116 return super.endBatchEdit ();
117 }
118
119 @Override
120 public boolean
121 commitText (CharSequence text, int newCursorPosition)
122 {
123 editable.compositionEnd ();
124 super.commitText (text, newCursorPosition);
125
126 /* An observation is that input methods rarely recompose trailing
127 spaces. Avoid re-setting the commit length in that case. */
128
129 if (text.toString ().equals (" "))
130 lastCommitLength += 1;
131 else
132 /* At this point, the editable is now empty.
133
134 The input method may try to cancel the edit upon a subsequent
135 backspace by calling setComposingRegion with a region that is
136 the length of TEXT.
137
138 Record this length in order to be able to send backspace
139 events to ``delete'' the text in that case. */
140 lastCommitLength = text.length ();
141
142 Log.d (TAG, "commitText: \"" + text + "\"");
143
144 return true;
145 }
146
147 /* Return a large offset, cycling through 100000, 30000, 0.
148 The offset is typically used to force the input method to update
149 its notion of ``surrounding text'', bypassing any caching that
150 it might have in progress.
151
152 There must be another way to do this, but I can't find it. */
153
154 public int
155 largeSelectionOffset ()
156 {
157 switch (currentLargeOffset)
158 {
159 case 0:
160 currentLargeOffset = 100000;
161 return 100000;
162
163 case 100000:
164 currentLargeOffset = 30000;
165 return 30000;
166
167 case 30000:
168 currentLargeOffset = 0;
169 return 0;
170 }
171
172 currentLargeOffset = 0;
173 return -1;
174 }
175}
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
index 939348ba420..f0219843d35 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -25,6 +25,10 @@ import android.content.res.AssetManager;
25 25
26public class EmacsNative 26public class EmacsNative
27{ 27{
28 /* List of native libraries that must be loaded during class
29 initialization. */
30 private static final String[] libraryDeps;
31
28 /* Obtain the fingerprint of this build of Emacs. The fingerprint 32 /* Obtain the fingerprint of this build of Emacs. The fingerprint
29 can be used to determine the dump file name. */ 33 can be used to determine the dump file name. */
30 public static native String getFingerprint (); 34 public static native String getFingerprint ();
@@ -167,148 +171,26 @@ public class EmacsNative
167 Every time you add a new shared library dependency to Emacs, 171 Every time you add a new shared library dependency to Emacs,
168 please add it here as well. */ 172 please add it here as well. */
169 173
170 try 174 libraryDeps = new String[] { "png_emacs", "selinux_emacs",
171 { 175 "crypto_emacs", "pcre_emacs",
172 System.loadLibrary ("png_emacs"); 176 "packagelistparser_emacs",
173 } 177 "gnutls_emacs", "gmp_emacs",
174 catch (UnsatisfiedLinkError exception) 178 "nettle_emacs", "p11-kit_emacs",
175 { 179 "tasn1_emacs", "hogweed_emacs",
176 /* Ignore this exception. */ 180 "jansson_emacs", "jpeg_emacs",
177 } 181 "tiff_emacs", "xml2_emacs",
178 182 "icuuc_emacs", };
179 try 183
180 { 184 for (String dependency : libraryDeps)
181 System.loadLibrary ("selinux_emacs"); 185 {
182 } 186 try
183 catch (UnsatisfiedLinkError exception) 187 {
184 { 188 System.loadLibrary (dependency);
185 /* Ignore this exception. */ 189 }
186 } 190 catch (UnsatisfiedLinkError exception)
187 191 {
188 try 192 /* Ignore this exception. */
189 { 193 }
190 System.loadLibrary ("crypto_emacs");
191 }
192 catch (UnsatisfiedLinkError exception)
193 {
194 /* Ignore this exception. */
195 }
196
197 try
198 {
199 System.loadLibrary ("pcre_emacs");
200 }
201 catch (UnsatisfiedLinkError exception)
202 {
203 /* Ignore this exception. */
204 }
205
206 try
207 {
208 System.loadLibrary ("packagelistparser_emacs");
209 }
210 catch (UnsatisfiedLinkError exception)
211 {
212 /* Ignore this exception. */
213 }
214
215 try
216 {
217 System.loadLibrary ("gnutls_emacs");
218 }
219 catch (UnsatisfiedLinkError exception)
220 {
221 /* Ignore this exception. */
222 }
223
224 try
225 {
226 System.loadLibrary ("gmp_emacs");
227 }
228 catch (UnsatisfiedLinkError exception)
229 {
230 /* Ignore this exception. */
231 }
232
233 try
234 {
235 System.loadLibrary ("nettle_emacs");
236 }
237 catch (UnsatisfiedLinkError exception)
238 {
239 /* Ignore this exception. */
240 }
241
242 try
243 {
244 System.loadLibrary ("p11-kit_emacs");
245 }
246 catch (UnsatisfiedLinkError exception)
247 {
248 /* Ignore this exception. */
249 }
250
251 try
252 {
253 System.loadLibrary ("tasn1_emacs");
254 }
255 catch (UnsatisfiedLinkError exception)
256 {
257 /* Ignore this exception. */
258 }
259
260 try
261 {
262 System.loadLibrary ("hogweed_emacs");
263 }
264 catch (UnsatisfiedLinkError exception)
265 {
266 /* Ignore this exception. */
267 }
268
269 try
270 {
271 System.loadLibrary ("jansson_emacs");
272 }
273 catch (UnsatisfiedLinkError exception)
274 {
275 /* Ignore this exception. */
276 }
277
278 try
279 {
280 System.loadLibrary ("jpeg_emacs");
281 }
282 catch (UnsatisfiedLinkError exception)
283 {
284 /* Ignore this exception. */
285 }
286
287 try
288 {
289 System.loadLibrary ("tiff_emacs");
290 }
291 catch (UnsatisfiedLinkError exception)
292 {
293 /* Ignore this exception. */
294 }
295
296 try
297 {
298 System.loadLibrary ("xml2_emacs");
299 }
300 catch (UnsatisfiedLinkError exception)
301 {
302 /* Ignore this exception. */
303 }
304
305 try
306 {
307 System.loadLibrary ("icuuc_emacs");
308 }
309 catch (UnsatisfiedLinkError exception)
310 {
311 /* Ignore this exception. */
312 } 194 }
313 195
314 System.loadLibrary ("emacs"); 196 System.loadLibrary ("emacs");
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
index 4fc8104e31f..bc3716f6da8 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -94,7 +94,7 @@ public class EmacsView extends ViewGroup
94 private long lastClipSerial; 94 private long lastClipSerial;
95 95
96 /* The InputMethodManager for this view's context. */ 96 /* The InputMethodManager for this view's context. */
97 private InputMethodManager imManager; 97 public InputMethodManager imManager;
98 98
99 /* Whether or not this view is attached to a window. */ 99 /* Whether or not this view is attached to a window. */
100 public boolean isAttachedToWindow; 100 public boolean isAttachedToWindow;
@@ -558,8 +558,9 @@ public class EmacsView extends ViewGroup
558 box that obscures Emacs. */ 558 box that obscures Emacs. */
559 info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; 559 info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
560 560
561 /* But don't return an InputConnection, in order to force the on 561 /* Set a reasonable inputType. */
562 screen keyboard to work correctly. */ 562 info.inputType = InputType.TYPE_NULL;
563
563 return null; 564 return null;
564 } 565 }
565 566
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java
index 9e2f2f53270..0eca35cec61 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -23,6 +23,8 @@ import java.lang.IllegalStateException;
23import java.util.ArrayList; 23import java.util.ArrayList;
24import java.util.List; 24import java.util.List;
25import java.util.HashMap; 25import java.util.HashMap;
26import java.util.LinkedHashMap;
27import java.util.Map;
26 28
27import android.content.Context; 29import android.content.Context;
28 30
@@ -100,7 +102,7 @@ public class EmacsWindow extends EmacsHandleObject
100 102
101 /* The button state and keyboard modifier mask at the time of the 103 /* The button state and keyboard modifier mask at the time of the
102 last button press or release event. */ 104 last button press or release event. */
103 private int lastButtonState, lastModifiers; 105 public int lastButtonState, lastModifiers;
104 106
105 /* Whether or not the window is mapped, and whether or not it is 107 /* Whether or not the window is mapped, and whether or not it is
106 deiconified. */ 108 deiconified. */
@@ -122,6 +124,10 @@ public class EmacsWindow extends EmacsHandleObject
122 to quit Emacs. */ 124 to quit Emacs. */
123 private long lastVolumeButtonRelease; 125 private long lastVolumeButtonRelease;
124 126
127 /* Linked list of character strings which were recently sent as
128 events. */
129 public LinkedHashMap<Integer, String> eventStrings;
130
125 public 131 public
126 EmacsWindow (short handle, final EmacsWindow parent, int x, int y, 132 EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
127 int width, int height, boolean overrideRedirect) 133 int width, int height, boolean overrideRedirect)
@@ -155,6 +161,19 @@ public class EmacsWindow extends EmacsHandleObject
155 } 161 }
156 162
157 scratchGC = new EmacsGC ((short) 0); 163 scratchGC = new EmacsGC ((short) 0);
164
165 /* Create the map of input method-committed strings. Keep at most
166 ten strings in the map. */
167
168 eventStrings
169 = new LinkedHashMap<Integer, String> () {
170 @Override
171 protected boolean
172 removeEldestEntry (Map.Entry<Integer, String> entry)
173 {
174 return size () > 10;
175 }
176 };
158 } 177 }
159 178
160 public void 179 public void
@@ -507,10 +526,40 @@ public class EmacsWindow extends EmacsHandleObject
507 return view.getBitmap (); 526 return view.getBitmap ();
508 } 527 }
509 528
529 /* event.getCharacters is used because older input methods still
530 require it. */
531 @SuppressWarnings ("deprecation")
532 public int
533 getEventUnicodeChar (KeyEvent event, int state)
534 {
535 String characters;
536
537 if (event.getUnicodeChar (state) != 0)
538 return event.getUnicodeChar (state);
539
540 characters = event.getCharacters ();
541
542 if (characters != null && characters.length () == 1)
543 return characters.charAt (0);
544
545 return characters == null ? 0 : -1;
546 }
547
548 public void
549 saveUnicodeString (int serial, String string)
550 {
551 eventStrings.put (serial, string);
552 }
553
554 /* event.getCharacters is used because older input methods still
555 require it. */
556 @SuppressWarnings ("deprecation")
510 public void 557 public void
511 onKeyDown (int keyCode, KeyEvent event) 558 onKeyDown (int keyCode, KeyEvent event)
512 { 559 {
513 int state, state_1; 560 int state, state_1;
561 long serial;
562 String characters;
514 563
515 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) 564 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
516 state = event.getModifiers (); 565 state = event.getModifiers ();
@@ -537,18 +586,28 @@ public class EmacsWindow extends EmacsHandleObject
537 state_1 586 state_1
538 = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK); 587 = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
539 588
540 EmacsNative.sendKeyPress (this.handle, 589 synchronized (eventStrings)
541 event.getEventTime (), 590 {
542 state, keyCode, 591 serial
543 event.getUnicodeChar (state_1)); 592 = EmacsNative.sendKeyPress (this.handle,
544 lastModifiers = state; 593 event.getEventTime (),
594 state, keyCode,
595 getEventUnicodeChar (event,
596 state_1));
597 lastModifiers = state;
598
599 characters = event.getCharacters ();
600
601 if (characters != null && characters.length () > 1)
602 saveUnicodeString ((int) serial, characters);
603 }
545 } 604 }
546 605
547 public void 606 public void
548 onKeyUp (int keyCode, KeyEvent event) 607 onKeyUp (int keyCode, KeyEvent event)
549 { 608 {
550 int state, state_1; 609 int state, state_1;
551 long time; 610 long time, serial;
552 611
553 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) 612 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
554 state = event.getModifiers (); 613 state = event.getModifiers ();
@@ -575,10 +634,12 @@ public class EmacsWindow extends EmacsHandleObject
575 state_1 634 state_1
576 = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK); 635 = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
577 636
578 EmacsNative.sendKeyRelease (this.handle, 637 serial
579 event.getEventTime (), 638 = EmacsNative.sendKeyRelease (this.handle,
580 state, keyCode, 639 event.getEventTime (),
581 event.getUnicodeChar (state_1)); 640 state, keyCode,
641 getEventUnicodeChar (event,
642 state_1));
582 lastModifiers = state; 643 lastModifiers = state;
583 644
584 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) 645 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
@@ -1105,4 +1166,17 @@ public class EmacsWindow extends EmacsHandleObject
1105 } 1166 }
1106 }); 1167 });
1107 } 1168 }
1169
1170 public String
1171 lookupString (int eventSerial)
1172 {
1173 String any;
1174
1175 synchronized (eventStrings)
1176 {
1177 any = eventStrings.remove (eventSerial);
1178 }
1179
1180 return any;
1181 }
1108}; 1182};