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