blob: fc672454a0a0876135cc14fb7b3d84846a2b0a62 [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017package com.android.internal.view;
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.os.Bundle;
Yohei Yukawa612cce92016-02-11 17:47:33 -080020import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.os.RemoteException;
22import android.os.SystemClock;
23import android.util.Log;
24import android.view.KeyEvent;
25import android.view.inputmethod.CompletionInfo;
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -080026import android.view.inputmethod.CorrectionInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.view.inputmethod.ExtractedText;
28import android.view.inputmethod.ExtractedTextRequest;
29import android.view.inputmethod.InputConnection;
30
31public class InputConnectionWrapper implements InputConnection {
32 private static final int MAX_WAIT_TIME_MILLIS = 2000;
33 private final IInputContext mIInputContext;
34
35 static class InputContextCallback extends IInputContextCallback.Stub {
36 private static final String TAG = "InputConnectionWrapper.ICC";
37 public int mSeq;
38 public boolean mHaveValue;
39 public CharSequence mTextBeforeCursor;
40 public CharSequence mTextAfterCursor;
Amith Yamasania90b7f02010-08-25 18:27:20 -070041 public CharSequence mSelectedText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 public ExtractedText mExtractedText;
43 public int mCursorCapsMode;
Yohei Yukawaa277db22014-08-21 18:38:44 -070044 public boolean mRequestUpdateCursorAnchorInfoResult;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
46 // A 'pool' of one InputContextCallback. Each ICW request will attempt to gain
47 // exclusive access to this object.
48 private static InputContextCallback sInstance = new InputContextCallback();
49 private static int sSequenceNumber = 1;
50
51 /**
52 * Returns an InputContextCallback object that is guaranteed not to be in use by
53 * any other thread. The returned object's 'have value' flag is cleared and its expected
54 * sequence number is set to a new integer. We use a sequence number so that replies that
55 * occur after a timeout has expired are not interpreted as replies to a later request.
56 */
57 private static InputContextCallback getInstance() {
58 synchronized (InputContextCallback.class) {
59 // Return sInstance if it's non-null, otherwise construct a new callback
60 InputContextCallback callback;
61 if (sInstance != null) {
62 callback = sInstance;
63 sInstance = null;
64
65 // Reset the callback
66 callback.mHaveValue = false;
67 } else {
68 callback = new InputContextCallback();
69 }
70
71 // Set the sequence number
72 callback.mSeq = sSequenceNumber++;
73 return callback;
74 }
75 }
76
77 /**
78 * Makes the given InputContextCallback available for use in the future.
79 */
80 private void dispose() {
81 synchronized (InputContextCallback.class) {
82 // If sInstance is non-null, just let this object be garbage-collected
83 if (sInstance == null) {
84 // Allow any objects being held to be gc'ed
85 mTextAfterCursor = null;
86 mTextBeforeCursor = null;
87 mExtractedText = null;
88 sInstance = this;
89 }
90 }
91 }
92
93 public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) {
94 synchronized (this) {
95 if (seq == mSeq) {
96 mTextBeforeCursor = textBeforeCursor;
97 mHaveValue = true;
98 notifyAll();
99 } else {
100 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
101 + ") in setTextBeforeCursor, ignoring.");
102 }
103 }
104 }
105
106 public void setTextAfterCursor(CharSequence textAfterCursor, int seq) {
107 synchronized (this) {
108 if (seq == mSeq) {
109 mTextAfterCursor = textAfterCursor;
110 mHaveValue = true;
111 notifyAll();
112 } else {
113 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
114 + ") in setTextAfterCursor, ignoring.");
115 }
116 }
117 }
118
Amith Yamasania90b7f02010-08-25 18:27:20 -0700119 public void setSelectedText(CharSequence selectedText, int seq) {
120 synchronized (this) {
121 if (seq == mSeq) {
122 mSelectedText = selectedText;
123 mHaveValue = true;
124 notifyAll();
125 } else {
126 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
127 + ") in setSelectedText, ignoring.");
128 }
129 }
130 }
131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 public void setCursorCapsMode(int capsMode, int seq) {
133 synchronized (this) {
134 if (seq == mSeq) {
135 mCursorCapsMode = capsMode;
136 mHaveValue = true;
137 notifyAll();
138 } else {
139 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
140 + ") in setCursorCapsMode, ignoring.");
141 }
142 }
143 }
144
145 public void setExtractedText(ExtractedText extractedText, int seq) {
146 synchronized (this) {
147 if (seq == mSeq) {
148 mExtractedText = extractedText;
149 mHaveValue = true;
150 notifyAll();
151 } else {
152 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
153 + ") in setExtractedText, ignoring.");
154 }
155 }
156 }
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900157
Yohei Yukawaa277db22014-08-21 18:38:44 -0700158 public void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq) {
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900159 synchronized (this) {
160 if (seq == mSeq) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700161 mRequestUpdateCursorAnchorInfoResult = result;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900162 mHaveValue = true;
163 notifyAll();
164 } else {
165 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
166 + ") in setCursorAnchorInfoRequestResult, ignoring.");
167 }
168 }
169 }
170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 /**
172 * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
173 *
174 * <p>The caller must be synchronized on this callback object.
175 */
176 void waitForResultLocked() {
177 long startTime = SystemClock.uptimeMillis();
178 long endTime = startTime + MAX_WAIT_TIME_MILLIS;
179
180 while (!mHaveValue) {
181 long remainingTime = endTime - SystemClock.uptimeMillis();
182 if (remainingTime <= 0) {
183 Log.w(TAG, "Timed out waiting on IInputContextCallback");
184 return;
185 }
186 try {
187 wait(remainingTime);
188 } catch (InterruptedException e) {
189 }
190 }
191 }
192 }
193
194 public InputConnectionWrapper(IInputContext inputContext) {
195 mIInputContext = inputContext;
196 }
197
198 public CharSequence getTextAfterCursor(int length, int flags) {
199 CharSequence value = null;
200 try {
201 InputContextCallback callback = InputContextCallback.getInstance();
202 mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback);
203 synchronized (callback) {
204 callback.waitForResultLocked();
205 if (callback.mHaveValue) {
206 value = callback.mTextAfterCursor;
207 }
208 }
209 callback.dispose();
210 } catch (RemoteException e) {
211 return null;
212 }
213 return value;
214 }
215
216 public CharSequence getTextBeforeCursor(int length, int flags) {
217 CharSequence value = null;
218 try {
219 InputContextCallback callback = InputContextCallback.getInstance();
220 mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback);
221 synchronized (callback) {
222 callback.waitForResultLocked();
223 if (callback.mHaveValue) {
224 value = callback.mTextBeforeCursor;
225 }
226 }
227 callback.dispose();
228 } catch (RemoteException e) {
229 return null;
230 }
231 return value;
232 }
233
Amith Yamasania90b7f02010-08-25 18:27:20 -0700234 public CharSequence getSelectedText(int flags) {
235 CharSequence value = null;
236 try {
237 InputContextCallback callback = InputContextCallback.getInstance();
238 mIInputContext.getSelectedText(flags, callback.mSeq, callback);
239 synchronized (callback) {
240 callback.waitForResultLocked();
241 if (callback.mHaveValue) {
242 value = callback.mSelectedText;
243 }
244 }
245 callback.dispose();
246 } catch (RemoteException e) {
247 return null;
248 }
249 return value;
250 }
251
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 public int getCursorCapsMode(int reqModes) {
253 int value = 0;
254 try {
255 InputContextCallback callback = InputContextCallback.getInstance();
256 mIInputContext.getCursorCapsMode(reqModes, callback.mSeq, callback);
257 synchronized (callback) {
258 callback.waitForResultLocked();
259 if (callback.mHaveValue) {
260 value = callback.mCursorCapsMode;
261 }
262 }
263 callback.dispose();
264 } catch (RemoteException e) {
265 return 0;
266 }
267 return value;
268 }
satoke3797a12011-03-22 06:34:48 +0900269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
271 ExtractedText value = null;
272 try {
273 InputContextCallback callback = InputContextCallback.getInstance();
274 mIInputContext.getExtractedText(request, flags, callback.mSeq, callback);
275 synchronized (callback) {
276 callback.waitForResultLocked();
277 if (callback.mHaveValue) {
278 value = callback.mExtractedText;
279 }
280 }
281 callback.dispose();
282 } catch (RemoteException e) {
283 return null;
284 }
285 return value;
286 }
287
288 public boolean commitText(CharSequence text, int newCursorPosition) {
289 try {
290 mIInputContext.commitText(text, newCursorPosition);
291 return true;
292 } catch (RemoteException e) {
293 return false;
294 }
295 }
296
297 public boolean commitCompletion(CompletionInfo text) {
298 try {
299 mIInputContext.commitCompletion(text);
300 return true;
301 } catch (RemoteException e) {
302 return false;
303 }
304 }
305
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -0800306 public boolean commitCorrection(CorrectionInfo correctionInfo) {
307 try {
308 mIInputContext.commitCorrection(correctionInfo);
309 return true;
310 } catch (RemoteException e) {
311 return false;
312 }
313 }
314
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 public boolean setSelection(int start, int end) {
316 try {
317 mIInputContext.setSelection(start, end);
318 return true;
319 } catch (RemoteException e) {
320 return false;
321 }
322 }
323
324 public boolean performEditorAction(int actionCode) {
325 try {
326 mIInputContext.performEditorAction(actionCode);
327 return true;
328 } catch (RemoteException e) {
329 return false;
330 }
331 }
332
333 public boolean performContextMenuAction(int id) {
334 try {
335 mIInputContext.performContextMenuAction(id);
336 return true;
337 } catch (RemoteException e) {
338 return false;
339 }
340 }
Amith Yamasania90b7f02010-08-25 18:27:20 -0700341
342 public boolean setComposingRegion(int start, int end) {
343 try {
344 mIInputContext.setComposingRegion(start, end);
345 return true;
346 } catch (RemoteException e) {
347 return false;
348 }
349 }
350
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 public boolean setComposingText(CharSequence text, int newCursorPosition) {
352 try {
353 mIInputContext.setComposingText(text, newCursorPosition);
354 return true;
355 } catch (RemoteException e) {
356 return false;
357 }
358 }
359
360 public boolean finishComposingText() {
361 try {
362 mIInputContext.finishComposingText();
363 return true;
364 } catch (RemoteException e) {
365 return false;
366 }
367 }
368
369 public boolean beginBatchEdit() {
370 try {
371 mIInputContext.beginBatchEdit();
372 return true;
373 } catch (RemoteException e) {
374 return false;
375 }
376 }
377
378 public boolean endBatchEdit() {
379 try {
380 mIInputContext.endBatchEdit();
381 return true;
382 } catch (RemoteException e) {
383 return false;
384 }
385 }
386
387 public boolean sendKeyEvent(KeyEvent event) {
388 try {
389 mIInputContext.sendKeyEvent(event);
390 return true;
391 } catch (RemoteException e) {
392 return false;
393 }
394 }
395
396 public boolean clearMetaKeyStates(int states) {
397 try {
398 mIInputContext.clearMetaKeyStates(states);
399 return true;
400 } catch (RemoteException e) {
401 return false;
402 }
403 }
Yohei Yukawac89e22a2016-01-13 22:48:14 -0800404
Fabrice Di Meglio0c95dd32012-01-23 15:06:42 -0800405 public boolean deleteSurroundingText(int beforeLength, int afterLength) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 try {
Fabrice Di Meglio0c95dd32012-01-23 15:06:42 -0800407 mIInputContext.deleteSurroundingText(beforeLength, afterLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 return true;
409 } catch (RemoteException e) {
410 return false;
411 }
412 }
413
Yohei Yukawac89e22a2016-01-13 22:48:14 -0800414 public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
415 try {
416 mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
417 return true;
418 } catch (RemoteException e) {
419 return false;
420 }
421 }
422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 public boolean reportFullscreenMode(boolean enabled) {
424 try {
425 mIInputContext.reportFullscreenMode(enabled);
426 return true;
427 } catch (RemoteException e) {
428 return false;
429 }
430 }
431
432 public boolean performPrivateCommand(String action, Bundle data) {
433 try {
434 mIInputContext.performPrivateCommand(action, data);
435 return true;
436 } catch (RemoteException e) {
437 return false;
438 }
439 }
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900440
Yohei Yukawad8636ea2014-09-02 22:03:30 -0700441 public boolean requestCursorUpdates(int cursorUpdateMode) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700442 boolean result = false;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900443 try {
444 InputContextCallback callback = InputContextCallback.getInstance();
Yohei Yukawaa277db22014-08-21 18:38:44 -0700445 mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode, callback.mSeq, callback);
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900446 synchronized (callback) {
447 callback.waitForResultLocked();
448 if (callback.mHaveValue) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700449 result = callback.mRequestUpdateCursorAnchorInfoResult;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900450 }
451 }
452 callback.dispose();
453 } catch (RemoteException e) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700454 return false;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900455 }
Yohei Yukawaa277db22014-08-21 18:38:44 -0700456 return result;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900457 }
Yohei Yukawa612cce92016-02-11 17:47:33 -0800458
459 public Handler getHandler() {
460 // Nothing should happen when called from input method.
461 return null;
462 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463}