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