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