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