blob: a235d9a2346f3730f672a408aef22420c0761b04 [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;
43
44 // A 'pool' of one InputContextCallback. Each ICW request will attempt to gain
45 // exclusive access to this object.
46 private static InputContextCallback sInstance = new InputContextCallback();
47 private static int sSequenceNumber = 1;
48
49 /**
50 * Returns an InputContextCallback object that is guaranteed not to be in use by
51 * any other thread. The returned object's 'have value' flag is cleared and its expected
52 * sequence number is set to a new integer. We use a sequence number so that replies that
53 * occur after a timeout has expired are not interpreted as replies to a later request.
54 */
55 private static InputContextCallback getInstance() {
56 synchronized (InputContextCallback.class) {
57 // Return sInstance if it's non-null, otherwise construct a new callback
58 InputContextCallback callback;
59 if (sInstance != null) {
60 callback = sInstance;
61 sInstance = null;
62
63 // Reset the callback
64 callback.mHaveValue = false;
65 } else {
66 callback = new InputContextCallback();
67 }
68
69 // Set the sequence number
70 callback.mSeq = sSequenceNumber++;
71 return callback;
72 }
73 }
74
75 /**
76 * Makes the given InputContextCallback available for use in the future.
77 */
78 private void dispose() {
79 synchronized (InputContextCallback.class) {
80 // If sInstance is non-null, just let this object be garbage-collected
81 if (sInstance == null) {
82 // Allow any objects being held to be gc'ed
83 mTextAfterCursor = null;
84 mTextBeforeCursor = null;
85 mExtractedText = null;
86 sInstance = this;
87 }
88 }
89 }
90
91 public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) {
92 synchronized (this) {
93 if (seq == mSeq) {
94 mTextBeforeCursor = textBeforeCursor;
95 mHaveValue = true;
96 notifyAll();
97 } else {
98 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
99 + ") in setTextBeforeCursor, ignoring.");
100 }
101 }
102 }
103
104 public void setTextAfterCursor(CharSequence textAfterCursor, int seq) {
105 synchronized (this) {
106 if (seq == mSeq) {
107 mTextAfterCursor = textAfterCursor;
108 mHaveValue = true;
109 notifyAll();
110 } else {
111 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
112 + ") in setTextAfterCursor, ignoring.");
113 }
114 }
115 }
116
Amith Yamasania90b7f02010-08-25 18:27:20 -0700117 public void setSelectedText(CharSequence selectedText, int seq) {
118 synchronized (this) {
119 if (seq == mSeq) {
120 mSelectedText = selectedText;
121 mHaveValue = true;
122 notifyAll();
123 } else {
124 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
125 + ") in setSelectedText, ignoring.");
126 }
127 }
128 }
129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 public void setCursorCapsMode(int capsMode, int seq) {
131 synchronized (this) {
132 if (seq == mSeq) {
133 mCursorCapsMode = capsMode;
134 mHaveValue = true;
135 notifyAll();
136 } else {
137 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
138 + ") in setCursorCapsMode, ignoring.");
139 }
140 }
141 }
142
143 public void setExtractedText(ExtractedText extractedText, int seq) {
144 synchronized (this) {
145 if (seq == mSeq) {
146 mExtractedText = extractedText;
147 mHaveValue = true;
148 notifyAll();
149 } else {
150 Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
151 + ") in setExtractedText, ignoring.");
152 }
153 }
154 }
155
156 /**
157 * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
158 *
159 * <p>The caller must be synchronized on this callback object.
160 */
161 void waitForResultLocked() {
162 long startTime = SystemClock.uptimeMillis();
163 long endTime = startTime + MAX_WAIT_TIME_MILLIS;
164
165 while (!mHaveValue) {
166 long remainingTime = endTime - SystemClock.uptimeMillis();
167 if (remainingTime <= 0) {
168 Log.w(TAG, "Timed out waiting on IInputContextCallback");
169 return;
170 }
171 try {
172 wait(remainingTime);
173 } catch (InterruptedException e) {
174 }
175 }
176 }
177 }
178
179 public InputConnectionWrapper(IInputContext inputContext) {
180 mIInputContext = inputContext;
181 }
182
183 public CharSequence getTextAfterCursor(int length, int flags) {
184 CharSequence value = null;
185 try {
186 InputContextCallback callback = InputContextCallback.getInstance();
187 mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback);
188 synchronized (callback) {
189 callback.waitForResultLocked();
190 if (callback.mHaveValue) {
191 value = callback.mTextAfterCursor;
192 }
193 }
194 callback.dispose();
195 } catch (RemoteException e) {
196 return null;
197 }
198 return value;
199 }
200
201 public CharSequence getTextBeforeCursor(int length, int flags) {
202 CharSequence value = null;
203 try {
204 InputContextCallback callback = InputContextCallback.getInstance();
205 mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback);
206 synchronized (callback) {
207 callback.waitForResultLocked();
208 if (callback.mHaveValue) {
209 value = callback.mTextBeforeCursor;
210 }
211 }
212 callback.dispose();
213 } catch (RemoteException e) {
214 return null;
215 }
216 return value;
217 }
218
Amith Yamasania90b7f02010-08-25 18:27:20 -0700219 public CharSequence getSelectedText(int flags) {
220 CharSequence value = null;
221 try {
222 InputContextCallback callback = InputContextCallback.getInstance();
223 mIInputContext.getSelectedText(flags, callback.mSeq, callback);
224 synchronized (callback) {
225 callback.waitForResultLocked();
226 if (callback.mHaveValue) {
227 value = callback.mSelectedText;
228 }
229 }
230 callback.dispose();
231 } catch (RemoteException e) {
232 return null;
233 }
234 return value;
235 }
236
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 public int getCursorCapsMode(int reqModes) {
238 int value = 0;
239 try {
240 InputContextCallback callback = InputContextCallback.getInstance();
241 mIInputContext.getCursorCapsMode(reqModes, callback.mSeq, callback);
242 synchronized (callback) {
243 callback.waitForResultLocked();
244 if (callback.mHaveValue) {
245 value = callback.mCursorCapsMode;
246 }
247 }
248 callback.dispose();
249 } catch (RemoteException e) {
250 return 0;
251 }
252 return value;
253 }
satoke3797a12011-03-22 06:34:48 +0900254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
256 ExtractedText value = null;
257 try {
258 InputContextCallback callback = InputContextCallback.getInstance();
259 mIInputContext.getExtractedText(request, flags, callback.mSeq, callback);
260 synchronized (callback) {
261 callback.waitForResultLocked();
262 if (callback.mHaveValue) {
263 value = callback.mExtractedText;
264 }
265 }
266 callback.dispose();
267 } catch (RemoteException e) {
268 return null;
269 }
270 return value;
271 }
272
273 public boolean commitText(CharSequence text, int newCursorPosition) {
274 try {
275 mIInputContext.commitText(text, newCursorPosition);
276 return true;
277 } catch (RemoteException e) {
278 return false;
279 }
280 }
281
282 public boolean commitCompletion(CompletionInfo text) {
283 try {
284 mIInputContext.commitCompletion(text);
285 return true;
286 } catch (RemoteException e) {
287 return false;
288 }
289 }
290
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -0800291 public boolean commitCorrection(CorrectionInfo correctionInfo) {
292 try {
293 mIInputContext.commitCorrection(correctionInfo);
294 return true;
295 } catch (RemoteException e) {
296 return false;
297 }
298 }
299
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 public boolean setSelection(int start, int end) {
301 try {
302 mIInputContext.setSelection(start, end);
303 return true;
304 } catch (RemoteException e) {
305 return false;
306 }
307 }
308
309 public boolean performEditorAction(int actionCode) {
310 try {
311 mIInputContext.performEditorAction(actionCode);
312 return true;
313 } catch (RemoteException e) {
314 return false;
315 }
316 }
317
318 public boolean performContextMenuAction(int id) {
319 try {
320 mIInputContext.performContextMenuAction(id);
321 return true;
322 } catch (RemoteException e) {
323 return false;
324 }
325 }
Amith Yamasania90b7f02010-08-25 18:27:20 -0700326
327 public boolean setComposingRegion(int start, int end) {
328 try {
329 mIInputContext.setComposingRegion(start, end);
330 return true;
331 } catch (RemoteException e) {
332 return false;
333 }
334 }
335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 public boolean setComposingText(CharSequence text, int newCursorPosition) {
337 try {
338 mIInputContext.setComposingText(text, newCursorPosition);
339 return true;
340 } catch (RemoteException e) {
341 return false;
342 }
343 }
344
345 public boolean finishComposingText() {
346 try {
347 mIInputContext.finishComposingText();
348 return true;
349 } catch (RemoteException e) {
350 return false;
351 }
352 }
353
354 public boolean beginBatchEdit() {
355 try {
356 mIInputContext.beginBatchEdit();
357 return true;
358 } catch (RemoteException e) {
359 return false;
360 }
361 }
362
363 public boolean endBatchEdit() {
364 try {
365 mIInputContext.endBatchEdit();
366 return true;
367 } catch (RemoteException e) {
368 return false;
369 }
370 }
371
372 public boolean sendKeyEvent(KeyEvent event) {
373 try {
374 mIInputContext.sendKeyEvent(event);
375 return true;
376 } catch (RemoteException e) {
377 return false;
378 }
379 }
380
381 public boolean clearMetaKeyStates(int states) {
382 try {
383 mIInputContext.clearMetaKeyStates(states);
384 return true;
385 } catch (RemoteException e) {
386 return false;
387 }
388 }
389
390 public boolean deleteSurroundingText(int leftLength, int rightLength) {
391 try {
392 mIInputContext.deleteSurroundingText(leftLength, rightLength);
393 return true;
394 } catch (RemoteException e) {
395 return false;
396 }
397 }
398
399 public boolean reportFullscreenMode(boolean enabled) {
400 try {
401 mIInputContext.reportFullscreenMode(enabled);
402 return true;
403 } catch (RemoteException e) {
404 return false;
405 }
406 }
407
408 public boolean performPrivateCommand(String action, Bundle data) {
409 try {
410 mIInputContext.performPrivateCommand(action, data);
411 return true;
412 } catch (RemoteException e) {
413 return false;
414 }
415 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416}