blob: 0c65ad17bb3676bdfd28ab7799bb955ee6f1b91f [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;
20import android.os.RemoteException;
21import android.os.SystemClock;
22import android.util.Log;
23import android.view.KeyEvent;
24import android.view.inputmethod.CompletionInfo;
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -080025import android.view.inputmethod.CorrectionInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.view.inputmethod.ExtractedText;
27import android.view.inputmethod.ExtractedTextRequest;
28import android.view.inputmethod.InputConnection;
29
30public class InputConnectionWrapper implements InputConnection {
31 private static final int MAX_WAIT_TIME_MILLIS = 2000;
32 private final IInputContext mIInputContext;
33
34 static class InputContextCallback extends IInputContextCallback.Stub {
35 private static final String TAG = "InputConnectionWrapper.ICC";
36 public int mSeq;
37 public boolean mHaveValue;
38 public CharSequence mTextBeforeCursor;
39 public CharSequence mTextAfterCursor;
Amith Yamasania90b7f02010-08-25 18:27:20 -070040 public CharSequence mSelectedText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 public ExtractedText mExtractedText;
42 public int mCursorCapsMode;
Yohei Yukawaa277db22014-08-21 18:38:44 -070043 public boolean mRequestUpdateCursorAnchorInfoResult;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
45 // A 'pool' of one InputContextCallback. Each ICW request will attempt to gain
46 // exclusive access to this object.
47 private static InputContextCallback sInstance = new InputContextCallback();
48 private static int sSequenceNumber = 1;
49
50 /**
51 * Returns an InputContextCallback object that is guaranteed not to be in use by
52 * any other thread. The returned object's 'have value' flag is cleared and its expected
53 * sequence number is set to a new integer. We use a sequence number so that replies that
54 * occur after a timeout has expired are not interpreted as replies to a later request.
55 */
56 private static InputContextCallback getInstance() {
57 synchronized (InputContextCallback.class) {
58 // Return sInstance if it's non-null, otherwise construct a new callback
59 InputContextCallback callback;
60 if (sInstance != null) {
61 callback = sInstance;
62 sInstance = null;
63
64 // Reset the callback
65 callback.mHaveValue = false;
66 } else {
67 callback = new InputContextCallback();
68 }
69
70 // Set the sequence number
71 callback.mSeq = sSequenceNumber++;
72 return callback;
73 }
74 }
75
76 /**
77 * Makes the given InputContextCallback available for use in the future.
78 */
79 private void dispose() {
80 synchronized (InputContextCallback.class) {
81 // If sInstance is non-null, just let this object be garbage-collected
82 if (sInstance == null) {
83 // Allow any objects being held to be gc'ed
84 mTextAfterCursor = null;
85 mTextBeforeCursor = null;
86 mExtractedText = null;
87 sInstance = this;
88 }
89 }
90 }
91
92 public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) {
93 synchronized (this) {
94 if (seq == mSeq) {
95 mTextBeforeCursor = textBeforeCursor;
96 mHaveValue = true;
97 notifyAll();
98 } else {
99 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
100 + ") in setTextBeforeCursor, ignoring.");
101 }
102 }
103 }
104
105 public void setTextAfterCursor(CharSequence textAfterCursor, int seq) {
106 synchronized (this) {
107 if (seq == mSeq) {
108 mTextAfterCursor = textAfterCursor;
109 mHaveValue = true;
110 notifyAll();
111 } else {
112 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
113 + ") in setTextAfterCursor, ignoring.");
114 }
115 }
116 }
117
Amith Yamasania90b7f02010-08-25 18:27:20 -0700118 public void setSelectedText(CharSequence selectedText, int seq) {
119 synchronized (this) {
120 if (seq == mSeq) {
121 mSelectedText = selectedText;
122 mHaveValue = true;
123 notifyAll();
124 } else {
125 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
126 + ") in setSelectedText, ignoring.");
127 }
128 }
129 }
130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 public void setCursorCapsMode(int capsMode, int seq) {
132 synchronized (this) {
133 if (seq == mSeq) {
134 mCursorCapsMode = capsMode;
135 mHaveValue = true;
136 notifyAll();
137 } else {
138 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
139 + ") in setCursorCapsMode, ignoring.");
140 }
141 }
142 }
143
144 public void setExtractedText(ExtractedText extractedText, int seq) {
145 synchronized (this) {
146 if (seq == mSeq) {
147 mExtractedText = extractedText;
148 mHaveValue = true;
149 notifyAll();
150 } else {
151 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
152 + ") in setExtractedText, ignoring.");
153 }
154 }
155 }
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900156
Yohei Yukawaa277db22014-08-21 18:38:44 -0700157 public void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq) {
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900158 synchronized (this) {
159 if (seq == mSeq) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700160 mRequestUpdateCursorAnchorInfoResult = result;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900161 mHaveValue = true;
162 notifyAll();
163 } else {
164 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
165 + ") in setCursorAnchorInfoRequestResult, ignoring.");
166 }
167 }
168 }
169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 /**
171 * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
172 *
173 * <p>The caller must be synchronized on this callback object.
174 */
175 void waitForResultLocked() {
176 long startTime = SystemClock.uptimeMillis();
177 long endTime = startTime + MAX_WAIT_TIME_MILLIS;
178
179 while (!mHaveValue) {
180 long remainingTime = endTime - SystemClock.uptimeMillis();
181 if (remainingTime <= 0) {
182 Log.w(TAG, "Timed out waiting on IInputContextCallback");
183 return;
184 }
185 try {
186 wait(remainingTime);
187 } catch (InterruptedException e) {
188 }
189 }
190 }
191 }
192
193 public InputConnectionWrapper(IInputContext inputContext) {
194 mIInputContext = inputContext;
195 }
196
197 public CharSequence getTextAfterCursor(int length, int flags) {
198 CharSequence value = null;
199 try {
200 InputContextCallback callback = InputContextCallback.getInstance();
201 mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback);
202 synchronized (callback) {
203 callback.waitForResultLocked();
204 if (callback.mHaveValue) {
205 value = callback.mTextAfterCursor;
206 }
207 }
208 callback.dispose();
209 } catch (RemoteException e) {
210 return null;
211 }
212 return value;
213 }
214
215 public CharSequence getTextBeforeCursor(int length, int flags) {
216 CharSequence value = null;
217 try {
218 InputContextCallback callback = InputContextCallback.getInstance();
219 mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback);
220 synchronized (callback) {
221 callback.waitForResultLocked();
222 if (callback.mHaveValue) {
223 value = callback.mTextBeforeCursor;
224 }
225 }
226 callback.dispose();
227 } catch (RemoteException e) {
228 return null;
229 }
230 return value;
231 }
232
Amith Yamasania90b7f02010-08-25 18:27:20 -0700233 public CharSequence getSelectedText(int flags) {
234 CharSequence value = null;
235 try {
236 InputContextCallback callback = InputContextCallback.getInstance();
237 mIInputContext.getSelectedText(flags, callback.mSeq, callback);
238 synchronized (callback) {
239 callback.waitForResultLocked();
240 if (callback.mHaveValue) {
241 value = callback.mSelectedText;
242 }
243 }
244 callback.dispose();
245 } catch (RemoteException e) {
246 return null;
247 }
248 return value;
249 }
250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 public int getCursorCapsMode(int reqModes) {
252 int value = 0;
253 try {
254 InputContextCallback callback = InputContextCallback.getInstance();
255 mIInputContext.getCursorCapsMode(reqModes, callback.mSeq, callback);
256 synchronized (callback) {
257 callback.waitForResultLocked();
258 if (callback.mHaveValue) {
259 value = callback.mCursorCapsMode;
260 }
261 }
262 callback.dispose();
263 } catch (RemoteException e) {
264 return 0;
265 }
266 return value;
267 }
satoke3797a12011-03-22 06:34:48 +0900268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
270 ExtractedText value = null;
271 try {
272 InputContextCallback callback = InputContextCallback.getInstance();
273 mIInputContext.getExtractedText(request, flags, callback.mSeq, callback);
274 synchronized (callback) {
275 callback.waitForResultLocked();
276 if (callback.mHaveValue) {
277 value = callback.mExtractedText;
278 }
279 }
280 callback.dispose();
281 } catch (RemoteException e) {
282 return null;
283 }
284 return value;
285 }
286
287 public boolean commitText(CharSequence text, int newCursorPosition) {
288 try {
289 mIInputContext.commitText(text, newCursorPosition);
290 return true;
291 } catch (RemoteException e) {
292 return false;
293 }
294 }
295
296 public boolean commitCompletion(CompletionInfo text) {
297 try {
298 mIInputContext.commitCompletion(text);
299 return true;
300 } catch (RemoteException e) {
301 return false;
302 }
303 }
304
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -0800305 public boolean commitCorrection(CorrectionInfo correctionInfo) {
306 try {
307 mIInputContext.commitCorrection(correctionInfo);
308 return true;
309 } catch (RemoteException e) {
310 return false;
311 }
312 }
313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 public boolean setSelection(int start, int end) {
315 try {
316 mIInputContext.setSelection(start, end);
317 return true;
318 } catch (RemoteException e) {
319 return false;
320 }
321 }
322
323 public boolean performEditorAction(int actionCode) {
324 try {
325 mIInputContext.performEditorAction(actionCode);
326 return true;
327 } catch (RemoteException e) {
328 return false;
329 }
330 }
331
332 public boolean performContextMenuAction(int id) {
333 try {
334 mIInputContext.performContextMenuAction(id);
335 return true;
336 } catch (RemoteException e) {
337 return false;
338 }
339 }
Amith Yamasania90b7f02010-08-25 18:27:20 -0700340
341 public boolean setComposingRegion(int start, int end) {
342 try {
343 mIInputContext.setComposingRegion(start, end);
344 return true;
345 } catch (RemoteException e) {
346 return false;
347 }
348 }
349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 public boolean setComposingText(CharSequence text, int newCursorPosition) {
351 try {
352 mIInputContext.setComposingText(text, newCursorPosition);
353 return true;
354 } catch (RemoteException e) {
355 return false;
356 }
357 }
358
359 public boolean finishComposingText() {
360 try {
361 mIInputContext.finishComposingText();
362 return true;
363 } catch (RemoteException e) {
364 return false;
365 }
366 }
367
368 public boolean beginBatchEdit() {
369 try {
370 mIInputContext.beginBatchEdit();
371 return true;
372 } catch (RemoteException e) {
373 return false;
374 }
375 }
376
377 public boolean endBatchEdit() {
378 try {
379 mIInputContext.endBatchEdit();
380 return true;
381 } catch (RemoteException e) {
382 return false;
383 }
384 }
385
386 public boolean sendKeyEvent(KeyEvent event) {
387 try {
388 mIInputContext.sendKeyEvent(event);
389 return true;
390 } catch (RemoteException e) {
391 return false;
392 }
393 }
394
395 public boolean clearMetaKeyStates(int states) {
396 try {
397 mIInputContext.clearMetaKeyStates(states);
398 return true;
399 } catch (RemoteException e) {
400 return false;
401 }
402 }
403
Fabrice Di Meglio0c95dd32012-01-23 15:06:42 -0800404 public boolean deleteSurroundingText(int beforeLength, int afterLength) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 try {
Fabrice Di Meglio0c95dd32012-01-23 15:06:42 -0800406 mIInputContext.deleteSurroundingText(beforeLength, afterLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 return true;
408 } catch (RemoteException e) {
409 return false;
410 }
411 }
412
413 public boolean reportFullscreenMode(boolean enabled) {
414 try {
415 mIInputContext.reportFullscreenMode(enabled);
416 return true;
417 } catch (RemoteException e) {
418 return false;
419 }
420 }
421
422 public boolean performPrivateCommand(String action, Bundle data) {
423 try {
424 mIInputContext.performPrivateCommand(action, data);
425 return true;
426 } catch (RemoteException e) {
427 return false;
428 }
429 }
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900430
Yohei Yukawad8636ea2014-09-02 22:03:30 -0700431 public boolean requestCursorUpdates(int cursorUpdateMode) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700432 boolean result = false;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900433 try {
434 InputContextCallback callback = InputContextCallback.getInstance();
Yohei Yukawaa277db22014-08-21 18:38:44 -0700435 mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode, callback.mSeq, callback);
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900436 synchronized (callback) {
437 callback.waitForResultLocked();
438 if (callback.mHaveValue) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700439 result = callback.mRequestUpdateCursorAnchorInfoResult;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900440 }
441 }
442 callback.dispose();
443 } catch (RemoteException e) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700444 return false;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900445 }
Yohei Yukawaa277db22014-08-21 18:38:44 -0700446 return result;
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900447 }
Yohei Yukawad8636ea2014-09-02 22:03:30 -0700448
449 /**
450 * @removed
451 */
452 public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) {
453 return requestCursorUpdates(cursorUpdateMode);
454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455}