IInputConnectionWrapper.java revision 0023d0e0c4f5339b299d1eacbd4e7181c2fd271f
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.CursorAnchorInfoRequest;
29import android.view.inputmethod.ExtractedTextRequest;
30import android.view.inputmethod.InputConnection;
31
32import java.lang.ref.WeakReference;
33
34public class IInputConnectionWrapper extends IInputContext.Stub {
35    static final String TAG = "IInputConnectionWrapper";
36
37    private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
38    private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
39    private static final int DO_GET_SELECTED_TEXT = 25;
40    private static final int DO_GET_CURSOR_CAPS_MODE = 30;
41    private static final int DO_GET_EXTRACTED_TEXT = 40;
42    private static final int DO_COMMIT_TEXT = 50;
43    private static final int DO_COMMIT_COMPLETION = 55;
44    private static final int DO_COMMIT_CORRECTION = 56;
45    private static final int DO_SET_SELECTION = 57;
46    private static final int DO_PERFORM_EDITOR_ACTION = 58;
47    private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
48    private static final int DO_SET_COMPOSING_TEXT = 60;
49    private static final int DO_SET_COMPOSING_REGION = 63;
50    private static final int DO_FINISH_COMPOSING_TEXT = 65;
51    private static final int DO_SEND_KEY_EVENT = 70;
52    private static final int DO_DELETE_SURROUNDING_TEXT = 80;
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_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 leftLength, int rightLength) {
160        dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
161            leftLength, rightLength));
162    }
163
164    public void beginBatchEdit() {
165        dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
166    }
167
168    public void endBatchEdit() {
169        dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
170    }
171
172    public void reportFullscreenMode(boolean enabled) {
173        dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0));
174    }
175
176    public void performPrivateCommand(String action, Bundle data) {
177        dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
178    }
179
180    public void requestCursorAnchorInfo(CursorAnchorInfoRequest request, int seq,
181            IInputContextCallback callback) {
182        dispatchMessage(obtainMessageOSC(DO_REQUEST_CURSOR_ANCHOR_INFO, request, seq, callback));
183    }
184
185    void dispatchMessage(Message msg) {
186        // If we are calling this from the main thread, then we can call
187        // right through.  Otherwise, we need to send the message to the
188        // main thread.
189        if (Looper.myLooper() == mMainLooper) {
190            executeMessage(msg);
191            msg.recycle();
192            return;
193        }
194
195        mH.sendMessage(msg);
196    }
197
198    void executeMessage(Message msg) {
199        switch (msg.what) {
200            case DO_GET_TEXT_AFTER_CURSOR: {
201                SomeArgs args = (SomeArgs)msg.obj;
202                try {
203                    InputConnection ic = mInputConnection.get();
204                    if (ic == null || !isActive()) {
205                        Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
206                        args.callback.setTextAfterCursor(null, args.seq);
207                        return;
208                    }
209                    args.callback.setTextAfterCursor(ic.getTextAfterCursor(
210                            msg.arg1, msg.arg2), args.seq);
211                } catch (RemoteException e) {
212                    Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e);
213                }
214                return;
215            }
216            case DO_GET_TEXT_BEFORE_CURSOR: {
217                SomeArgs args = (SomeArgs)msg.obj;
218                try {
219                    InputConnection ic = mInputConnection.get();
220                    if (ic == null || !isActive()) {
221                        Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
222                        args.callback.setTextBeforeCursor(null, args.seq);
223                        return;
224                    }
225                    args.callback.setTextBeforeCursor(ic.getTextBeforeCursor(
226                            msg.arg1, msg.arg2), args.seq);
227                } catch (RemoteException e) {
228                    Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e);
229                }
230                return;
231            }
232            case DO_GET_SELECTED_TEXT: {
233                SomeArgs args = (SomeArgs)msg.obj;
234                try {
235                    InputConnection ic = mInputConnection.get();
236                    if (ic == null || !isActive()) {
237                        Log.w(TAG, "getSelectedText on inactive InputConnection");
238                        args.callback.setSelectedText(null, args.seq);
239                        return;
240                    }
241                    args.callback.setSelectedText(ic.getSelectedText(
242                            msg.arg1), args.seq);
243                } catch (RemoteException e) {
244                    Log.w(TAG, "Got RemoteException calling setSelectedText", e);
245                }
246                return;
247            }
248            case DO_GET_CURSOR_CAPS_MODE: {
249                SomeArgs args = (SomeArgs)msg.obj;
250                try {
251                    InputConnection ic = mInputConnection.get();
252                    if (ic == null || !isActive()) {
253                        Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
254                        args.callback.setCursorCapsMode(0, args.seq);
255                        return;
256                    }
257                    args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1),
258                            args.seq);
259                } catch (RemoteException e) {
260                    Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e);
261                }
262                return;
263            }
264            case DO_GET_EXTRACTED_TEXT: {
265                SomeArgs args = (SomeArgs)msg.obj;
266                try {
267                    InputConnection ic = mInputConnection.get();
268                    if (ic == null || !isActive()) {
269                        Log.w(TAG, "getExtractedText on inactive InputConnection");
270                        args.callback.setExtractedText(null, args.seq);
271                        return;
272                    }
273                    args.callback.setExtractedText(ic.getExtractedText(
274                            (ExtractedTextRequest)args.arg1, msg.arg1), args.seq);
275                } catch (RemoteException e) {
276                    Log.w(TAG, "Got RemoteException calling setExtractedText", e);
277                }
278                return;
279            }
280            case DO_COMMIT_TEXT: {
281                InputConnection ic = mInputConnection.get();
282                if (ic == null || !isActive()) {
283                    Log.w(TAG, "commitText on inactive InputConnection");
284                    return;
285                }
286                ic.commitText((CharSequence)msg.obj, msg.arg1);
287                return;
288            }
289            case DO_SET_SELECTION: {
290                InputConnection ic = mInputConnection.get();
291                if (ic == null || !isActive()) {
292                    Log.w(TAG, "setSelection on inactive InputConnection");
293                    return;
294                }
295                ic.setSelection(msg.arg1, msg.arg2);
296                return;
297            }
298            case DO_PERFORM_EDITOR_ACTION: {
299                InputConnection ic = mInputConnection.get();
300                if (ic == null || !isActive()) {
301                    Log.w(TAG, "performEditorAction on inactive InputConnection");
302                    return;
303                }
304                ic.performEditorAction(msg.arg1);
305                return;
306            }
307            case DO_PERFORM_CONTEXT_MENU_ACTION: {
308                InputConnection ic = mInputConnection.get();
309                if (ic == null || !isActive()) {
310                    Log.w(TAG, "performContextMenuAction on inactive InputConnection");
311                    return;
312                }
313                ic.performContextMenuAction(msg.arg1);
314                return;
315            }
316            case DO_COMMIT_COMPLETION: {
317                InputConnection ic = mInputConnection.get();
318                if (ic == null || !isActive()) {
319                    Log.w(TAG, "commitCompletion on inactive InputConnection");
320                    return;
321                }
322                ic.commitCompletion((CompletionInfo)msg.obj);
323                return;
324            }
325            case DO_COMMIT_CORRECTION: {
326                InputConnection ic = mInputConnection.get();
327                if (ic == null || !isActive()) {
328                    Log.w(TAG, "commitCorrection on inactive InputConnection");
329                    return;
330                }
331                ic.commitCorrection((CorrectionInfo)msg.obj);
332                return;
333            }
334            case DO_SET_COMPOSING_TEXT: {
335                InputConnection ic = mInputConnection.get();
336                if (ic == null || !isActive()) {
337                    Log.w(TAG, "setComposingText on inactive InputConnection");
338                    return;
339                }
340                ic.setComposingText((CharSequence)msg.obj, msg.arg1);
341                return;
342            }
343            case DO_SET_COMPOSING_REGION: {
344                InputConnection ic = mInputConnection.get();
345                if (ic == null || !isActive()) {
346                    Log.w(TAG, "setComposingRegion on inactive InputConnection");
347                    return;
348                }
349                ic.setComposingRegion(msg.arg1, msg.arg2);
350                return;
351            }
352            case DO_FINISH_COMPOSING_TEXT: {
353                InputConnection ic = mInputConnection.get();
354                // Note we do NOT check isActive() here, because this is safe
355                // for an IME to call at any time, and we need to allow it
356                // through to clean up our state after the IME has switched to
357                // another client.
358                if (ic == null) {
359                    Log.w(TAG, "finishComposingText on inactive InputConnection");
360                    return;
361                }
362                ic.finishComposingText();
363                return;
364            }
365            case DO_SEND_KEY_EVENT: {
366                InputConnection ic = mInputConnection.get();
367                if (ic == null || !isActive()) {
368                    Log.w(TAG, "sendKeyEvent on inactive InputConnection");
369                    return;
370                }
371                ic.sendKeyEvent((KeyEvent)msg.obj);
372                return;
373            }
374            case DO_CLEAR_META_KEY_STATES: {
375                InputConnection ic = mInputConnection.get();
376                if (ic == null || !isActive()) {
377                    Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
378                    return;
379                }
380                ic.clearMetaKeyStates(msg.arg1);
381                return;
382            }
383            case DO_DELETE_SURROUNDING_TEXT: {
384                InputConnection ic = mInputConnection.get();
385                if (ic == null || !isActive()) {
386                    Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
387                    return;
388                }
389                ic.deleteSurroundingText(msg.arg1, msg.arg2);
390                return;
391            }
392            case DO_BEGIN_BATCH_EDIT: {
393                InputConnection ic = mInputConnection.get();
394                if (ic == null || !isActive()) {
395                    Log.w(TAG, "beginBatchEdit on inactive InputConnection");
396                    return;
397                }
398                ic.beginBatchEdit();
399                return;
400            }
401            case DO_END_BATCH_EDIT: {
402                InputConnection ic = mInputConnection.get();
403                if (ic == null || !isActive()) {
404                    Log.w(TAG, "endBatchEdit on inactive InputConnection");
405                    return;
406                }
407                ic.endBatchEdit();
408                return;
409            }
410            case DO_REPORT_FULLSCREEN_MODE: {
411                InputConnection ic = mInputConnection.get();
412                if (ic == null || !isActive()) {
413                    Log.w(TAG, "showStatusIcon on inactive InputConnection");
414                    return;
415                }
416                ic.reportFullscreenMode(msg.arg1 == 1);
417                return;
418            }
419            case DO_PERFORM_PRIVATE_COMMAND: {
420                InputConnection ic = mInputConnection.get();
421                if (ic == null || !isActive()) {
422                    Log.w(TAG, "performPrivateCommand on inactive InputConnection");
423                    return;
424                }
425                SomeArgs args = (SomeArgs)msg.obj;
426                ic.performPrivateCommand((String)args.arg1,
427                        (Bundle)args.arg2);
428                return;
429            }
430            case DO_REQUEST_CURSOR_ANCHOR_INFO: {
431                SomeArgs args = (SomeArgs)msg.obj;
432                try {
433                    InputConnection ic = mInputConnection.get();
434                    if (ic == null || !isActive()) {
435                        Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
436                        args.callback.setRequestCursorAnchorInfoResult(0, args.seq);
437                        return;
438                    }
439                    args.callback.setRequestCursorAnchorInfoResult(
440                            ic.requestCursorAnchorInfo((CursorAnchorInfoRequest)args.arg1),
441                            args.seq);
442                } catch (RemoteException e) {
443                    Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e);
444                }
445                return;
446            }
447        }
448        Log.w(TAG, "Unhandled message code: " + msg.what);
449    }
450
451    Message obtainMessage(int what) {
452        return mH.obtainMessage(what);
453    }
454
455    Message obtainMessageII(int what, int arg1, int arg2) {
456        return mH.obtainMessage(what, arg1, arg2);
457    }
458
459    Message obtainMessageO(int what, Object arg1) {
460        return mH.obtainMessage(what, 0, 0, arg1);
461    }
462
463    Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) {
464        SomeArgs args = new SomeArgs();
465        args.callback = callback;
466        args.seq = seq;
467        return mH.obtainMessage(what, arg1, 0, args);
468    }
469
470    Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) {
471        SomeArgs args = new SomeArgs();
472        args.callback = callback;
473        args.seq = seq;
474        return mH.obtainMessage(what, arg1, arg2, args);
475    }
476
477    Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) {
478        SomeArgs args = new SomeArgs();
479        args.arg1 = arg1;
480        args.callback = callback;
481        args.seq = seq;
482        return mH.obtainMessage(what, 0, 0, args);
483    }
484
485    Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
486            IInputContextCallback callback) {
487        SomeArgs args = new SomeArgs();
488        args.arg1 = arg2;
489        args.callback = callback;
490        args.seq = seq;
491        return mH.obtainMessage(what, arg1, 0, args);
492    }
493
494    Message obtainMessageIO(int what, int arg1, Object arg2) {
495        return mH.obtainMessage(what, arg1, 0, arg2);
496    }
497
498    Message obtainMessageOO(int what, Object arg1, Object arg2) {
499        SomeArgs args = new SomeArgs();
500        args.arg1 = arg1;
501        args.arg2 = arg2;
502        return mH.obtainMessage(what, 0, 0, args);
503    }
504}
505