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