blob: 0e7f06bc77b930bbb6be71160d33ba2e538565ff [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;
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -080027import android.view.inputmethod.CorrectionInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.view.inputmethod.ExtractedTextRequest;
29import android.view.inputmethod.InputConnection;
30
31import java.lang.ref.WeakReference;
32
Yohei Yukawa159dd472016-01-07 16:52:33 -080033public abstract class IInputConnectionWrapper extends IInputContext.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034 static final String TAG = "IInputConnectionWrapper";
Amith Yamasania90b7f02010-08-25 18:27:20 -070035
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036 private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
37 private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
Amith Yamasania90b7f02010-08-25 18:27:20 -070038 private static final int DO_GET_SELECTED_TEXT = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039 private static final int DO_GET_CURSOR_CAPS_MODE = 30;
40 private static final int DO_GET_EXTRACTED_TEXT = 40;
41 private static final int DO_COMMIT_TEXT = 50;
42 private static final int DO_COMMIT_COMPLETION = 55;
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -080043 private static final int DO_COMMIT_CORRECTION = 56;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 private static final int DO_SET_SELECTION = 57;
45 private static final int DO_PERFORM_EDITOR_ACTION = 58;
46 private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
47 private static final int DO_SET_COMPOSING_TEXT = 60;
Amith Yamasania90b7f02010-08-25 18:27:20 -070048 private static final int DO_SET_COMPOSING_REGION = 63;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 private static final int DO_FINISH_COMPOSING_TEXT = 65;
50 private static final int DO_SEND_KEY_EVENT = 70;
51 private static final int DO_DELETE_SURROUNDING_TEXT = 80;
52 private static final int DO_BEGIN_BATCH_EDIT = 90;
53 private static final int DO_END_BATCH_EDIT = 95;
54 private static final int DO_REPORT_FULLSCREEN_MODE = 100;
55 private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
56 private static final int DO_CLEAR_META_KEY_STATES = 130;
Yohei Yukawaa277db22014-08-21 18:38:44 -070057 private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
Amith Yamasania90b7f02010-08-25 18:27:20 -070058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 private WeakReference<InputConnection> mInputConnection;
60
61 private Looper mMainLooper;
62 private Handler mH;
63
64 static class SomeArgs {
65 Object arg1;
66 Object arg2;
67 IInputContextCallback callback;
68 int seq;
69 }
70
71 class MyHandler extends Handler {
72 MyHandler(Looper looper) {
73 super(looper);
74 }
75
76 @Override
77 public void handleMessage(Message msg) {
78 executeMessage(msg);
79 }
80 }
81
82 public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
Yohei Yukawa159dd472016-01-07 16:52:33 -080083 mInputConnection = new WeakReference<>(conn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 mMainLooper = mainLooper;
85 mH = new MyHandler(mMainLooper);
86 }
87
Yohei Yukawa159dd472016-01-07 16:52:33 -080088 abstract protected boolean isActive();
89
90 /**
91 * Called when the user took some actions that should be taken into consideration to update the
92 * LRU list for input method rotation.
93 */
94 abstract protected void onUserAction();
95
96 /**
97 * Called when the input method started or stopped full-screen mode.
98 *
99 */
100 abstract protected void onReportFullscreenMode(boolean enabled);
101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
103 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
104 }
105
106 public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) {
107 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
108 }
109
Amith Yamasania90b7f02010-08-25 18:27:20 -0700110 public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
111 dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
112 }
113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
115 dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
116 }
117
118 public void getExtractedText(ExtractedTextRequest request,
119 int flags, int seq, IInputContextCallback callback) {
120 dispatchMessage(obtainMessageIOSC(DO_GET_EXTRACTED_TEXT, flags,
121 request, seq, callback));
122 }
123
124 public void commitText(CharSequence text, int newCursorPosition) {
125 dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
126 }
127
128 public void commitCompletion(CompletionInfo text) {
129 dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text));
130 }
131
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -0800132 public void commitCorrection(CorrectionInfo info) {
133 dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info));
134 }
135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 public void setSelection(int start, int end) {
137 dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end));
138 }
139
140 public void performEditorAction(int id) {
141 dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0));
142 }
143
144 public void performContextMenuAction(int id) {
145 dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
146 }
147
Amith Yamasania90b7f02010-08-25 18:27:20 -0700148 public void setComposingRegion(int start, int end) {
149 dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
150 }
151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 public void setComposingText(CharSequence text, int newCursorPosition) {
153 dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
154 }
155
156 public void finishComposingText() {
157 dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
158 }
159
160 public void sendKeyEvent(KeyEvent event) {
161 dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
162 }
163
164 public void clearMetaKeyStates(int states) {
165 dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
166 }
167
168 public void deleteSurroundingText(int leftLength, int rightLength) {
169 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
170 leftLength, rightLength));
171 }
172
173 public void beginBatchEdit() {
174 dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
175 }
176
177 public void endBatchEdit() {
178 dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
179 }
180
181 public void reportFullscreenMode(boolean enabled) {
182 dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0));
183 }
184
185 public void performPrivateCommand(String action, Bundle data) {
186 dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
187 }
satokadb43582011-03-09 10:08:47 +0900188
Yohei Yukawaa277db22014-08-21 18:38:44 -0700189 public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900190 IInputContextCallback callback) {
Yohei Yukawaa277db22014-08-21 18:38:44 -0700191 dispatchMessage(obtainMessageISC(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
192 seq, callback));
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900193 }
194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 void dispatchMessage(Message msg) {
196 // If we are calling this from the main thread, then we can call
197 // right through. Otherwise, we need to send the message to the
198 // main thread.
199 if (Looper.myLooper() == mMainLooper) {
200 executeMessage(msg);
201 msg.recycle();
202 return;
203 }
204
205 mH.sendMessage(msg);
206 }
207
208 void executeMessage(Message msg) {
209 switch (msg.what) {
210 case DO_GET_TEXT_AFTER_CURSOR: {
211 SomeArgs args = (SomeArgs)msg.obj;
212 try {
213 InputConnection ic = mInputConnection.get();
214 if (ic == null || !isActive()) {
215 Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
216 args.callback.setTextAfterCursor(null, args.seq);
217 return;
218 }
219 args.callback.setTextAfterCursor(ic.getTextAfterCursor(
220 msg.arg1, msg.arg2), args.seq);
221 } catch (RemoteException e) {
222 Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e);
223 }
224 return;
225 }
226 case DO_GET_TEXT_BEFORE_CURSOR: {
227 SomeArgs args = (SomeArgs)msg.obj;
228 try {
229 InputConnection ic = mInputConnection.get();
230 if (ic == null || !isActive()) {
231 Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
232 args.callback.setTextBeforeCursor(null, args.seq);
233 return;
234 }
235 args.callback.setTextBeforeCursor(ic.getTextBeforeCursor(
236 msg.arg1, msg.arg2), args.seq);
237 } catch (RemoteException e) {
238 Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e);
239 }
240 return;
241 }
Amith Yamasania90b7f02010-08-25 18:27:20 -0700242 case DO_GET_SELECTED_TEXT: {
243 SomeArgs args = (SomeArgs)msg.obj;
244 try {
245 InputConnection ic = mInputConnection.get();
246 if (ic == null || !isActive()) {
247 Log.w(TAG, "getSelectedText on inactive InputConnection");
248 args.callback.setSelectedText(null, args.seq);
249 return;
250 }
251 args.callback.setSelectedText(ic.getSelectedText(
252 msg.arg1), args.seq);
253 } catch (RemoteException e) {
254 Log.w(TAG, "Got RemoteException calling setSelectedText", e);
255 }
256 return;
257 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 case DO_GET_CURSOR_CAPS_MODE: {
259 SomeArgs args = (SomeArgs)msg.obj;
260 try {
261 InputConnection ic = mInputConnection.get();
262 if (ic == null || !isActive()) {
263 Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
264 args.callback.setCursorCapsMode(0, args.seq);
265 return;
266 }
267 args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1),
268 args.seq);
269 } catch (RemoteException e) {
270 Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e);
271 }
272 return;
273 }
274 case DO_GET_EXTRACTED_TEXT: {
275 SomeArgs args = (SomeArgs)msg.obj;
276 try {
277 InputConnection ic = mInputConnection.get();
278 if (ic == null || !isActive()) {
279 Log.w(TAG, "getExtractedText on inactive InputConnection");
280 args.callback.setExtractedText(null, args.seq);
281 return;
282 }
283 args.callback.setExtractedText(ic.getExtractedText(
284 (ExtractedTextRequest)args.arg1, msg.arg1), args.seq);
285 } catch (RemoteException e) {
286 Log.w(TAG, "Got RemoteException calling setExtractedText", e);
287 }
288 return;
289 }
290 case DO_COMMIT_TEXT: {
291 InputConnection ic = mInputConnection.get();
292 if (ic == null || !isActive()) {
293 Log.w(TAG, "commitText on inactive InputConnection");
294 return;
295 }
296 ic.commitText((CharSequence)msg.obj, msg.arg1);
Yohei Yukawa159dd472016-01-07 16:52:33 -0800297 onUserAction();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 return;
299 }
300 case DO_SET_SELECTION: {
301 InputConnection ic = mInputConnection.get();
302 if (ic == null || !isActive()) {
303 Log.w(TAG, "setSelection on inactive InputConnection");
304 return;
305 }
306 ic.setSelection(msg.arg1, msg.arg2);
307 return;
308 }
309 case DO_PERFORM_EDITOR_ACTION: {
310 InputConnection ic = mInputConnection.get();
311 if (ic == null || !isActive()) {
312 Log.w(TAG, "performEditorAction on inactive InputConnection");
313 return;
314 }
315 ic.performEditorAction(msg.arg1);
316 return;
317 }
318 case DO_PERFORM_CONTEXT_MENU_ACTION: {
319 InputConnection ic = mInputConnection.get();
320 if (ic == null || !isActive()) {
321 Log.w(TAG, "performContextMenuAction on inactive InputConnection");
322 return;
323 }
324 ic.performContextMenuAction(msg.arg1);
325 return;
326 }
327 case DO_COMMIT_COMPLETION: {
328 InputConnection ic = mInputConnection.get();
329 if (ic == null || !isActive()) {
330 Log.w(TAG, "commitCompletion on inactive InputConnection");
331 return;
332 }
333 ic.commitCompletion((CompletionInfo)msg.obj);
334 return;
335 }
Gilles Debunnecf9cf2f2010-12-08 17:43:58 -0800336 case DO_COMMIT_CORRECTION: {
337 InputConnection ic = mInputConnection.get();
338 if (ic == null || !isActive()) {
339 Log.w(TAG, "commitCorrection on inactive InputConnection");
340 return;
341 }
342 ic.commitCorrection((CorrectionInfo)msg.obj);
343 return;
344 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 case DO_SET_COMPOSING_TEXT: {
346 InputConnection ic = mInputConnection.get();
347 if (ic == null || !isActive()) {
348 Log.w(TAG, "setComposingText on inactive InputConnection");
349 return;
350 }
351 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
Yohei Yukawa159dd472016-01-07 16:52:33 -0800352 onUserAction();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 return;
354 }
Amith Yamasania90b7f02010-08-25 18:27:20 -0700355 case DO_SET_COMPOSING_REGION: {
356 InputConnection ic = mInputConnection.get();
357 if (ic == null || !isActive()) {
358 Log.w(TAG, "setComposingRegion on inactive InputConnection");
359 return;
360 }
361 ic.setComposingRegion(msg.arg1, msg.arg2);
362 return;
363 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 case DO_FINISH_COMPOSING_TEXT: {
365 InputConnection ic = mInputConnection.get();
366 // Note we do NOT check isActive() here, because this is safe
367 // for an IME to call at any time, and we need to allow it
368 // through to clean up our state after the IME has switched to
369 // another client.
370 if (ic == null) {
371 Log.w(TAG, "finishComposingText on inactive InputConnection");
372 return;
373 }
374 ic.finishComposingText();
375 return;
376 }
377 case DO_SEND_KEY_EVENT: {
378 InputConnection ic = mInputConnection.get();
379 if (ic == null || !isActive()) {
380 Log.w(TAG, "sendKeyEvent on inactive InputConnection");
381 return;
382 }
383 ic.sendKeyEvent((KeyEvent)msg.obj);
Yohei Yukawa159dd472016-01-07 16:52:33 -0800384 onUserAction();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 return;
386 }
387 case DO_CLEAR_META_KEY_STATES: {
388 InputConnection ic = mInputConnection.get();
389 if (ic == null || !isActive()) {
390 Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
391 return;
392 }
393 ic.clearMetaKeyStates(msg.arg1);
394 return;
395 }
396 case DO_DELETE_SURROUNDING_TEXT: {
397 InputConnection ic = mInputConnection.get();
398 if (ic == null || !isActive()) {
399 Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
400 return;
401 }
402 ic.deleteSurroundingText(msg.arg1, msg.arg2);
403 return;
404 }
405 case DO_BEGIN_BATCH_EDIT: {
406 InputConnection ic = mInputConnection.get();
407 if (ic == null || !isActive()) {
408 Log.w(TAG, "beginBatchEdit on inactive InputConnection");
409 return;
410 }
411 ic.beginBatchEdit();
412 return;
413 }
414 case DO_END_BATCH_EDIT: {
415 InputConnection ic = mInputConnection.get();
416 if (ic == null || !isActive()) {
417 Log.w(TAG, "endBatchEdit on inactive InputConnection");
418 return;
419 }
420 ic.endBatchEdit();
421 return;
422 }
423 case DO_REPORT_FULLSCREEN_MODE: {
424 InputConnection ic = mInputConnection.get();
Andrei Stingaceanuda589df2015-06-17 10:38:08 +0100425 if (ic == null) {
426 Log.w(TAG, "reportFullscreenMode on inexistent InputConnection");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 return;
428 }
Yohei Yukawa159dd472016-01-07 16:52:33 -0800429 final boolean enabled = msg.arg1 == 1;
430 ic.reportFullscreenMode(enabled);
431 onReportFullscreenMode(enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 return;
433 }
434 case DO_PERFORM_PRIVATE_COMMAND: {
435 InputConnection ic = mInputConnection.get();
436 if (ic == null || !isActive()) {
437 Log.w(TAG, "performPrivateCommand on inactive InputConnection");
438 return;
439 }
440 SomeArgs args = (SomeArgs)msg.obj;
441 ic.performPrivateCommand((String)args.arg1,
442 (Bundle)args.arg2);
443 return;
444 }
Yohei Yukawaa277db22014-08-21 18:38:44 -0700445 case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900446 SomeArgs args = (SomeArgs)msg.obj;
447 try {
448 InputConnection ic = mInputConnection.get();
449 if (ic == null || !isActive()) {
450 Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
Yohei Yukawaa277db22014-08-21 18:38:44 -0700451 args.callback.setRequestUpdateCursorAnchorInfoResult(false, args.seq);
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900452 return;
453 }
Yohei Yukawaa277db22014-08-21 18:38:44 -0700454 args.callback.setRequestUpdateCursorAnchorInfoResult(
Yohei Yukawad8636ea2014-09-02 22:03:30 -0700455 ic.requestCursorUpdates(msg.arg1), args.seq);
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900456 } catch (RemoteException e) {
457 Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e);
458 }
459 return;
460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 }
462 Log.w(TAG, "Unhandled message code: " + msg.what);
463 }
464
465 Message obtainMessage(int what) {
466 return mH.obtainMessage(what);
467 }
468
469 Message obtainMessageII(int what, int arg1, int arg2) {
470 return mH.obtainMessage(what, arg1, arg2);
471 }
472
473 Message obtainMessageO(int what, Object arg1) {
474 return mH.obtainMessage(what, 0, 0, arg1);
475 }
476
477 Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) {
478 SomeArgs args = new SomeArgs();
479 args.callback = callback;
480 args.seq = seq;
481 return mH.obtainMessage(what, arg1, 0, args);
482 }
483
484 Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) {
485 SomeArgs args = new SomeArgs();
486 args.callback = callback;
487 args.seq = seq;
488 return mH.obtainMessage(what, arg1, arg2, args);
489 }
Yohei Yukawa0023d0e2014-07-11 04:13:03 +0900490
491 Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) {
492 SomeArgs args = new SomeArgs();
493 args.arg1 = arg1;
494 args.callback = callback;
495 args.seq = seq;
496 return mH.obtainMessage(what, 0, 0, args);
497 }
498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
500 IInputContextCallback callback) {
501 SomeArgs args = new SomeArgs();
502 args.arg1 = arg2;
503 args.callback = callback;
504 args.seq = seq;
505 return mH.obtainMessage(what, arg1, 0, args);
506 }
507
508 Message obtainMessageIO(int what, int arg1, Object arg2) {
509 return mH.obtainMessage(what, arg1, 0, arg2);
510 }
511
512 Message obtainMessageOO(int what, Object arg1, Object arg2) {
513 SomeArgs args = new SomeArgs();
514 args.arg1 = arg1;
515 args.arg2 = arg2;
516 return mH.obtainMessage(what, 0, 0, args);
517 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518}