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