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