blob: 986ba3815eb665354b89dd0f068269fc723f20a2 [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
19import android.os.Bundle;
20import android.os.Handler;
21import android.os.Looper;
22import android.os.Message;
23import android.os.RemoteException;
24import android.util.Log;
25import android.view.KeyEvent;
26import android.view.inputmethod.CompletionInfo;
27import android.view.inputmethod.ExtractedTextRequest;
28import android.view.inputmethod.InputConnection;
29
30import java.lang.ref.WeakReference;
31
32public class IInputConnectionWrapper extends IInputContext.Stub {
33 static final String TAG = "IInputConnectionWrapper";
Amith Yamasania90b7f02010-08-25 18:27:20 -070034
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035 private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
36 private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
Amith Yamasania90b7f02010-08-25 18:27:20 -070037 private static final int DO_GET_SELECTED_TEXT = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 private static final int DO_GET_CURSOR_CAPS_MODE = 30;
39 private static final int DO_GET_EXTRACTED_TEXT = 40;
40 private static final int DO_COMMIT_TEXT = 50;
41 private static final int DO_COMMIT_COMPLETION = 55;
42 private static final int DO_SET_SELECTION = 57;
43 private static final int DO_PERFORM_EDITOR_ACTION = 58;
44 private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
45 private static final int DO_SET_COMPOSING_TEXT = 60;
Amith Yamasania90b7f02010-08-25 18:27:20 -070046 private static final int DO_SET_COMPOSING_REGION = 63;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 private static final int DO_FINISH_COMPOSING_TEXT = 65;
48 private static final int DO_SEND_KEY_EVENT = 70;
49 private static final int DO_DELETE_SURROUNDING_TEXT = 80;
50 private static final int DO_BEGIN_BATCH_EDIT = 90;
51 private static final int DO_END_BATCH_EDIT = 95;
52 private static final int DO_REPORT_FULLSCREEN_MODE = 100;
53 private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
54 private static final int DO_CLEAR_META_KEY_STATES = 130;
Amith Yamasania90b7f02010-08-25 18:27:20 -070055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private WeakReference<InputConnection> mInputConnection;
57
58 private Looper mMainLooper;
59 private Handler mH;
60
61 static class SomeArgs {
62 Object arg1;
63 Object arg2;
64 IInputContextCallback callback;
65 int seq;
66 }
67
68 class MyHandler extends Handler {
69 MyHandler(Looper looper) {
70 super(looper);
71 }
72
73 @Override
74 public void handleMessage(Message msg) {
75 executeMessage(msg);
76 }
77 }
78
79 public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
80 mInputConnection = new WeakReference<InputConnection>(conn);
81 mMainLooper = mainLooper;
82 mH = new MyHandler(mMainLooper);
83 }
84
85 public boolean isActive() {
86 return true;
87 }
88
89 public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
90 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
91 }
92
93 public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) {
94 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
95 }
96
Amith Yamasania90b7f02010-08-25 18:27:20 -070097 public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
98 dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
99 }
100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
102 dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
103 }
104
105 public void getExtractedText(ExtractedTextRequest request,
106 int flags, int seq, IInputContextCallback callback) {
107 dispatchMessage(obtainMessageIOSC(DO_GET_EXTRACTED_TEXT, flags,
108 request, seq, callback));
109 }
110
111 public void commitText(CharSequence text, int newCursorPosition) {
112 dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
113 }
114
115 public void commitCompletion(CompletionInfo text) {
116 dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text));
117 }
118
119 public void setSelection(int start, int end) {
120 dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end));
121 }
122
123 public void performEditorAction(int id) {
124 dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0));
125 }
126
127 public void performContextMenuAction(int id) {
128 dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
129 }
130
Amith Yamasania90b7f02010-08-25 18:27:20 -0700131 public void setComposingRegion(int start, int end) {
132 dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
133 }
134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 public void setComposingText(CharSequence text, int newCursorPosition) {
136 dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
137 }
138
139 public void finishComposingText() {
140 dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
141 }
142
143 public void sendKeyEvent(KeyEvent event) {
144 dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
145 }
146
147 public void clearMetaKeyStates(int states) {
148 dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
149 }
150
151 public void deleteSurroundingText(int leftLength, int rightLength) {
152 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
153 leftLength, rightLength));
154 }
155
156 public void beginBatchEdit() {
157 dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
158 }
159
160 public void endBatchEdit() {
161 dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
162 }
163
164 public void reportFullscreenMode(boolean enabled) {
165 dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0));
166 }
167
168 public void performPrivateCommand(String action, Bundle data) {
169 dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
170 }
171
172 void dispatchMessage(Message msg) {
173 // If we are calling this from the main thread, then we can call
174 // right through. Otherwise, we need to send the message to the
175 // main thread.
176 if (Looper.myLooper() == mMainLooper) {
177 executeMessage(msg);
178 msg.recycle();
179 return;
180 }
181
182 mH.sendMessage(msg);
183 }
184
185 void executeMessage(Message msg) {
186 switch (msg.what) {
187 case DO_GET_TEXT_AFTER_CURSOR: {
188 SomeArgs args = (SomeArgs)msg.obj;
189 try {
190 InputConnection ic = mInputConnection.get();
191 if (ic == null || !isActive()) {
192 Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
193 args.callback.setTextAfterCursor(null, args.seq);
194 return;
195 }
196 args.callback.setTextAfterCursor(ic.getTextAfterCursor(
197 msg.arg1, msg.arg2), args.seq);
198 } catch (RemoteException e) {
199 Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e);
200 }
201 return;
202 }
203 case DO_GET_TEXT_BEFORE_CURSOR: {
204 SomeArgs args = (SomeArgs)msg.obj;
205 try {
206 InputConnection ic = mInputConnection.get();
207 if (ic == null || !isActive()) {
208 Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
209 args.callback.setTextBeforeCursor(null, args.seq);
210 return;
211 }
212 args.callback.setTextBeforeCursor(ic.getTextBeforeCursor(
213 msg.arg1, msg.arg2), args.seq);
214 } catch (RemoteException e) {
215 Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e);
216 }
217 return;
218 }
Amith Yamasania90b7f02010-08-25 18:27:20 -0700219 case DO_GET_SELECTED_TEXT: {
220 SomeArgs args = (SomeArgs)msg.obj;
221 try {
222 InputConnection ic = mInputConnection.get();
223 if (ic == null || !isActive()) {
224 Log.w(TAG, "getSelectedText on inactive InputConnection");
225 args.callback.setSelectedText(null, args.seq);
226 return;
227 }
228 args.callback.setSelectedText(ic.getSelectedText(
229 msg.arg1), args.seq);
230 } catch (RemoteException e) {
231 Log.w(TAG, "Got RemoteException calling setSelectedText", e);
232 }
233 return;
234 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 case DO_GET_CURSOR_CAPS_MODE: {
236 SomeArgs args = (SomeArgs)msg.obj;
237 try {
238 InputConnection ic = mInputConnection.get();
239 if (ic == null || !isActive()) {
240 Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
241 args.callback.setCursorCapsMode(0, args.seq);
242 return;
243 }
244 args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1),
245 args.seq);
246 } catch (RemoteException e) {
247 Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e);
248 }
249 return;
250 }
251 case DO_GET_EXTRACTED_TEXT: {
252 SomeArgs args = (SomeArgs)msg.obj;
253 try {
254 InputConnection ic = mInputConnection.get();
255 if (ic == null || !isActive()) {
256 Log.w(TAG, "getExtractedText on inactive InputConnection");
257 args.callback.setExtractedText(null, args.seq);
258 return;
259 }
260 args.callback.setExtractedText(ic.getExtractedText(
261 (ExtractedTextRequest)args.arg1, msg.arg1), args.seq);
262 } catch (RemoteException e) {
263 Log.w(TAG, "Got RemoteException calling setExtractedText", e);
264 }
265 return;
266 }
267 case DO_COMMIT_TEXT: {
268 InputConnection ic = mInputConnection.get();
269 if (ic == null || !isActive()) {
270 Log.w(TAG, "commitText on inactive InputConnection");
271 return;
272 }
273 ic.commitText((CharSequence)msg.obj, msg.arg1);
274 return;
275 }
276 case DO_SET_SELECTION: {
277 InputConnection ic = mInputConnection.get();
278 if (ic == null || !isActive()) {
279 Log.w(TAG, "setSelection on inactive InputConnection");
280 return;
281 }
282 ic.setSelection(msg.arg1, msg.arg2);
283 return;
284 }
285 case DO_PERFORM_EDITOR_ACTION: {
286 InputConnection ic = mInputConnection.get();
287 if (ic == null || !isActive()) {
288 Log.w(TAG, "performEditorAction on inactive InputConnection");
289 return;
290 }
291 ic.performEditorAction(msg.arg1);
292 return;
293 }
294 case DO_PERFORM_CONTEXT_MENU_ACTION: {
295 InputConnection ic = mInputConnection.get();
296 if (ic == null || !isActive()) {
297 Log.w(TAG, "performContextMenuAction on inactive InputConnection");
298 return;
299 }
300 ic.performContextMenuAction(msg.arg1);
301 return;
302 }
303 case DO_COMMIT_COMPLETION: {
304 InputConnection ic = mInputConnection.get();
305 if (ic == null || !isActive()) {
306 Log.w(TAG, "commitCompletion on inactive InputConnection");
307 return;
308 }
309 ic.commitCompletion((CompletionInfo)msg.obj);
310 return;
311 }
312 case DO_SET_COMPOSING_TEXT: {
313 InputConnection ic = mInputConnection.get();
314 if (ic == null || !isActive()) {
315 Log.w(TAG, "setComposingText on inactive InputConnection");
316 return;
317 }
318 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
319 return;
320 }
Amith Yamasania90b7f02010-08-25 18:27:20 -0700321 case DO_SET_COMPOSING_REGION: {
322 InputConnection ic = mInputConnection.get();
323 if (ic == null || !isActive()) {
324 Log.w(TAG, "setComposingRegion on inactive InputConnection");
325 return;
326 }
327 ic.setComposingRegion(msg.arg1, msg.arg2);
328 return;
329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 case DO_FINISH_COMPOSING_TEXT: {
331 InputConnection ic = mInputConnection.get();
332 // Note we do NOT check isActive() here, because this is safe
333 // for an IME to call at any time, and we need to allow it
334 // through to clean up our state after the IME has switched to
335 // another client.
336 if (ic == null) {
337 Log.w(TAG, "finishComposingText on inactive InputConnection");
338 return;
339 }
340 ic.finishComposingText();
341 return;
342 }
343 case DO_SEND_KEY_EVENT: {
344 InputConnection ic = mInputConnection.get();
345 if (ic == null || !isActive()) {
346 Log.w(TAG, "sendKeyEvent on inactive InputConnection");
347 return;
348 }
349 ic.sendKeyEvent((KeyEvent)msg.obj);
350 return;
351 }
352 case DO_CLEAR_META_KEY_STATES: {
353 InputConnection ic = mInputConnection.get();
354 if (ic == null || !isActive()) {
355 Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
356 return;
357 }
358 ic.clearMetaKeyStates(msg.arg1);
359 return;
360 }
361 case DO_DELETE_SURROUNDING_TEXT: {
362 InputConnection ic = mInputConnection.get();
363 if (ic == null || !isActive()) {
364 Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
365 return;
366 }
367 ic.deleteSurroundingText(msg.arg1, msg.arg2);
368 return;
369 }
370 case DO_BEGIN_BATCH_EDIT: {
371 InputConnection ic = mInputConnection.get();
372 if (ic == null || !isActive()) {
373 Log.w(TAG, "beginBatchEdit on inactive InputConnection");
374 return;
375 }
376 ic.beginBatchEdit();
377 return;
378 }
379 case DO_END_BATCH_EDIT: {
380 InputConnection ic = mInputConnection.get();
381 if (ic == null || !isActive()) {
382 Log.w(TAG, "endBatchEdit on inactive InputConnection");
383 return;
384 }
385 ic.endBatchEdit();
386 return;
387 }
388 case DO_REPORT_FULLSCREEN_MODE: {
389 InputConnection ic = mInputConnection.get();
390 if (ic == null || !isActive()) {
391 Log.w(TAG, "showStatusIcon on inactive InputConnection");
392 return;
393 }
394 ic.reportFullscreenMode(msg.arg1 == 1);
395 return;
396 }
397 case DO_PERFORM_PRIVATE_COMMAND: {
398 InputConnection ic = mInputConnection.get();
399 if (ic == null || !isActive()) {
400 Log.w(TAG, "performPrivateCommand on inactive InputConnection");
401 return;
402 }
403 SomeArgs args = (SomeArgs)msg.obj;
404 ic.performPrivateCommand((String)args.arg1,
405 (Bundle)args.arg2);
406 return;
407 }
408 }
409 Log.w(TAG, "Unhandled message code: " + msg.what);
410 }
411
412 Message obtainMessage(int what) {
413 return mH.obtainMessage(what);
414 }
415
416 Message obtainMessageII(int what, int arg1, int arg2) {
417 return mH.obtainMessage(what, arg1, arg2);
418 }
419
420 Message obtainMessageO(int what, Object arg1) {
421 return mH.obtainMessage(what, 0, 0, arg1);
422 }
423
424 Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) {
425 SomeArgs args = new SomeArgs();
426 args.callback = callback;
427 args.seq = seq;
428 return mH.obtainMessage(what, arg1, 0, args);
429 }
430
431 Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) {
432 SomeArgs args = new SomeArgs();
433 args.callback = callback;
434 args.seq = seq;
435 return mH.obtainMessage(what, arg1, arg2, args);
436 }
437
438 Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
439 IInputContextCallback callback) {
440 SomeArgs args = new SomeArgs();
441 args.arg1 = arg2;
442 args.callback = callback;
443 args.seq = seq;
444 return mH.obtainMessage(what, arg1, 0, args);
445 }
446
447 Message obtainMessageIO(int what, int arg1, Object arg2) {
448 return mH.obtainMessage(what, arg1, 0, arg2);
449 }
450
451 Message obtainMessageOO(int what, Object arg1, Object arg2) {
452 SomeArgs args = new SomeArgs();
453 args.arg1 = arg1;
454 args.arg2 = arg2;
455 return mH.obtainMessage(what, 0, 0, args);
456 }
457}