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