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