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