IInputMethodSessionWrapper.java revision 6090995951c6e2e4dcf38102f01793f8a94166e1
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 android.inputmethodservice;
18
19import com.android.internal.os.HandlerCaller;
20import com.android.internal.os.SomeArgs;
21import com.android.internal.view.IInputMethodSession;
22
23import android.content.Context;
24import android.graphics.Rect;
25import android.os.Bundle;
26import android.os.Looper;
27import android.os.Message;
28import android.util.Log;
29import android.util.SparseArray;
30import android.view.InputChannel;
31import android.view.InputDevice;
32import android.view.InputEvent;
33import android.view.InputEventReceiver;
34import android.view.KeyEvent;
35import android.view.MotionEvent;
36import android.view.inputmethod.CompletionInfo;
37import android.view.inputmethod.ExtractedText;
38import android.view.inputmethod.InputMethodSession;
39
40class IInputMethodSessionWrapper extends IInputMethodSession.Stub
41        implements HandlerCaller.Callback {
42    private static final String TAG = "InputMethodWrapper";
43
44    private static final int DO_FINISH_INPUT = 60;
45    private static final int DO_DISPLAY_COMPLETIONS = 65;
46    private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
47    private static final int DO_UPDATE_SELECTION = 90;
48    private static final int DO_UPDATE_CURSOR = 95;
49    private static final int DO_APP_PRIVATE_COMMAND = 100;
50    private static final int DO_TOGGLE_SOFT_INPUT = 105;
51    private static final int DO_FINISH_SESSION = 110;
52    private static final int DO_VIEW_CLICKED = 115;
53
54    HandlerCaller mCaller;
55    InputMethodSession mInputMethodSession;
56    InputChannel mChannel;
57    ImeInputEventReceiver mReceiver;
58
59    public IInputMethodSessionWrapper(Context context,
60            InputMethodSession inputMethodSession, InputChannel channel) {
61        mCaller = new HandlerCaller(context, null,
62                this, true /*asyncHandler*/);
63        mInputMethodSession = inputMethodSession;
64        mChannel = channel;
65        if (channel != null) {
66            mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
67        }
68    }
69
70    public InputMethodSession getInternalInputMethodSession() {
71        return mInputMethodSession;
72    }
73
74    @Override
75    public void executeMessage(Message msg) {
76        if (mInputMethodSession == null) {
77            // The session has been finished. Args needs to be recycled
78            // for cases below.
79            switch (msg.what) {
80                case DO_UPDATE_SELECTION:
81                case DO_APP_PRIVATE_COMMAND: {
82                    SomeArgs args = (SomeArgs)msg.obj;
83                    args.recycle();
84                }
85            }
86            return;
87        }
88
89        switch (msg.what) {
90            case DO_FINISH_INPUT:
91                mInputMethodSession.finishInput();
92                return;
93            case DO_DISPLAY_COMPLETIONS:
94                mInputMethodSession.displayCompletions((CompletionInfo[])msg.obj);
95                return;
96            case DO_UPDATE_EXTRACTED_TEXT:
97                mInputMethodSession.updateExtractedText(msg.arg1,
98                        (ExtractedText)msg.obj);
99                return;
100            case DO_UPDATE_SELECTION: {
101                SomeArgs args = (SomeArgs)msg.obj;
102                mInputMethodSession.updateSelection(args.argi1, args.argi2,
103                        args.argi3, args.argi4, args.argi5, args.argi6);
104                args.recycle();
105                return;
106            }
107            case DO_UPDATE_CURSOR: {
108                mInputMethodSession.updateCursor((Rect)msg.obj);
109                return;
110            }
111            case DO_APP_PRIVATE_COMMAND: {
112                SomeArgs args = (SomeArgs)msg.obj;
113                mInputMethodSession.appPrivateCommand((String)args.arg1,
114                        (Bundle)args.arg2);
115                args.recycle();
116                return;
117            }
118            case DO_TOGGLE_SOFT_INPUT: {
119                mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
120                return;
121            }
122            case DO_FINISH_SESSION: {
123                doFinishSession();
124                return;
125            }
126            case DO_VIEW_CLICKED: {
127                mInputMethodSession.viewClicked(msg.arg1 == 1);
128                return;
129            }
130        }
131        Log.w(TAG, "Unhandled message code: " + msg.what);
132    }
133
134    private void doFinishSession() {
135        mInputMethodSession = null;
136        if (mReceiver != null) {
137            mReceiver.dispose();
138            mReceiver = null;
139        }
140        if (mChannel != null) {
141            mChannel.dispose();
142            mChannel = null;
143        }
144    }
145
146    @Override
147    public void finishInput() {
148        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
149    }
150
151    @Override
152    public void displayCompletions(CompletionInfo[] completions) {
153        mCaller.executeOrSendMessage(mCaller.obtainMessageO(
154                DO_DISPLAY_COMPLETIONS, completions));
155    }
156
157    @Override
158    public void updateExtractedText(int token, ExtractedText text) {
159        mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
160                DO_UPDATE_EXTRACTED_TEXT, token, text));
161    }
162
163    @Override
164    public void updateSelection(int oldSelStart, int oldSelEnd,
165            int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
166        mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
167                oldSelStart, oldSelEnd, newSelStart, newSelEnd,
168                candidatesStart, candidatesEnd));
169    }
170
171    @Override
172    public void viewClicked(boolean focusChanged) {
173        mCaller.executeOrSendMessage(
174                mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
175    }
176
177    @Override
178    public void updateCursor(Rect newCursor) {
179        mCaller.executeOrSendMessage(
180                mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
181    }
182
183    @Override
184    public void appPrivateCommand(String action, Bundle data) {
185        mCaller.executeOrSendMessage(
186                mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
187    }
188
189    @Override
190    public void toggleSoftInput(int showFlags, int hideFlags) {
191        mCaller.executeOrSendMessage(
192                mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
193    }
194
195    @Override
196    public void finishSession() {
197        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
198    }
199
200    private final class ImeInputEventReceiver extends InputEventReceiver
201            implements InputMethodSession.EventCallback {
202        private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>();
203
204        public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) {
205            super(inputChannel, looper);
206        }
207
208        @Override
209        public void onInputEvent(InputEvent event) {
210            if (mInputMethodSession == null) {
211                // The session has been finished.
212                finishInputEvent(event, false);
213                return;
214            }
215
216            final int seq = event.getSequenceNumber();
217            mPendingEvents.put(seq, event);
218            if (event instanceof KeyEvent) {
219                KeyEvent keyEvent = (KeyEvent)event;
220                mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this);
221            } else {
222                MotionEvent motionEvent = (MotionEvent)event;
223                if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
224                    mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this);
225                } else {
226                    mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this);
227                }
228            }
229        }
230
231        @Override
232        public void finishedEvent(int seq, boolean handled) {
233            int index = mPendingEvents.indexOfKey(seq);
234            if (index >= 0) {
235                InputEvent event = mPendingEvents.valueAt(index);
236                mPendingEvents.removeAt(index);
237                finishInputEvent(event, handled);
238            }
239        }
240    }
241}
242