IInputConnectionWrapper.java revision c89e22a6ff227089fde26daea186346029d1b32c
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.CorrectionInfo; 28import android.view.inputmethod.ExtractedTextRequest; 29import android.view.inputmethod.InputConnection; 30 31import java.lang.ref.WeakReference; 32 33public class IInputConnectionWrapper extends IInputContext.Stub { 34 static final String TAG = "IInputConnectionWrapper"; 35 36 private static final int DO_GET_TEXT_AFTER_CURSOR = 10; 37 private static final int DO_GET_TEXT_BEFORE_CURSOR = 20; 38 private static final int DO_GET_SELECTED_TEXT = 25; 39 private static final int DO_GET_CURSOR_CAPS_MODE = 30; 40 private static final int DO_GET_EXTRACTED_TEXT = 40; 41 private static final int DO_COMMIT_TEXT = 50; 42 private static final int DO_COMMIT_COMPLETION = 55; 43 private static final int DO_COMMIT_CORRECTION = 56; 44 private static final int DO_SET_SELECTION = 57; 45 private static final int DO_PERFORM_EDITOR_ACTION = 58; 46 private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59; 47 private static final int DO_SET_COMPOSING_TEXT = 60; 48 private static final int DO_SET_COMPOSING_REGION = 63; 49 private static final int DO_FINISH_COMPOSING_TEXT = 65; 50 private static final int DO_SEND_KEY_EVENT = 70; 51 private static final int DO_DELETE_SURROUNDING_TEXT = 80; 52 private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81; 53 private static final int DO_BEGIN_BATCH_EDIT = 90; 54 private static final int DO_END_BATCH_EDIT = 95; 55 private static final int DO_REPORT_FULLSCREEN_MODE = 100; 56 private static final int DO_PERFORM_PRIVATE_COMMAND = 120; 57 private static final int DO_CLEAR_META_KEY_STATES = 130; 58 private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; 59 60 private WeakReference<InputConnection> mInputConnection; 61 62 private Looper mMainLooper; 63 private Handler mH; 64 65 static class SomeArgs { 66 Object arg1; 67 Object arg2; 68 IInputContextCallback callback; 69 int seq; 70 } 71 72 class MyHandler extends Handler { 73 MyHandler(Looper looper) { 74 super(looper); 75 } 76 77 @Override 78 public void handleMessage(Message msg) { 79 executeMessage(msg); 80 } 81 } 82 83 public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) { 84 mInputConnection = new WeakReference<InputConnection>(conn); 85 mMainLooper = mainLooper; 86 mH = new MyHandler(mMainLooper); 87 } 88 89 public boolean isActive() { 90 return true; 91 } 92 93 public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) { 94 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback)); 95 } 96 97 public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) { 98 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback)); 99 } 100 101 public void getSelectedText(int flags, int seq, IInputContextCallback callback) { 102 dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback)); 103 } 104 105 public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) { 106 dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback)); 107 } 108 109 public void getExtractedText(ExtractedTextRequest request, 110 int flags, int seq, IInputContextCallback callback) { 111 dispatchMessage(obtainMessageIOSC(DO_GET_EXTRACTED_TEXT, flags, 112 request, seq, callback)); 113 } 114 115 public void commitText(CharSequence text, int newCursorPosition) { 116 dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text)); 117 } 118 119 public void commitCompletion(CompletionInfo text) { 120 dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text)); 121 } 122 123 public void commitCorrection(CorrectionInfo info) { 124 dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info)); 125 } 126 127 public void setSelection(int start, int end) { 128 dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); 129 } 130 131 public void performEditorAction(int id) { 132 dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0)); 133 } 134 135 public void performContextMenuAction(int id) { 136 dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); 137 } 138 139 public void setComposingRegion(int start, int end) { 140 dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end)); 141 } 142 143 public void setComposingText(CharSequence text, int newCursorPosition) { 144 dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text)); 145 } 146 147 public void finishComposingText() { 148 dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT)); 149 } 150 151 public void sendKeyEvent(KeyEvent event) { 152 dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event)); 153 } 154 155 public void clearMetaKeyStates(int states) { 156 dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0)); 157 } 158 159 public void deleteSurroundingText(int beforeLength, int afterLength) { 160 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT, 161 beforeLength, afterLength)); 162 } 163 164 public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { 165 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS, 166 beforeLength, afterLength)); 167 } 168 169 public void beginBatchEdit() { 170 dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT)); 171 } 172 173 public void endBatchEdit() { 174 dispatchMessage(obtainMessage(DO_END_BATCH_EDIT)); 175 } 176 177 public void reportFullscreenMode(boolean enabled) { 178 dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0)); 179 } 180 181 public void performPrivateCommand(String action, Bundle data) { 182 dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data)); 183 } 184 185 public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq, 186 IInputContextCallback callback) { 187 dispatchMessage(obtainMessageISC(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode, 188 seq, callback)); 189 } 190 191 void dispatchMessage(Message msg) { 192 // If we are calling this from the main thread, then we can call 193 // right through. Otherwise, we need to send the message to the 194 // main thread. 195 if (Looper.myLooper() == mMainLooper) { 196 executeMessage(msg); 197 msg.recycle(); 198 return; 199 } 200 201 mH.sendMessage(msg); 202 } 203 204 void executeMessage(Message msg) { 205 switch (msg.what) { 206 case DO_GET_TEXT_AFTER_CURSOR: { 207 SomeArgs args = (SomeArgs)msg.obj; 208 try { 209 InputConnection ic = mInputConnection.get(); 210 if (ic == null || !isActive()) { 211 Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); 212 args.callback.setTextAfterCursor(null, args.seq); 213 return; 214 } 215 args.callback.setTextAfterCursor(ic.getTextAfterCursor( 216 msg.arg1, msg.arg2), args.seq); 217 } catch (RemoteException e) { 218 Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e); 219 } 220 return; 221 } 222 case DO_GET_TEXT_BEFORE_CURSOR: { 223 SomeArgs args = (SomeArgs)msg.obj; 224 try { 225 InputConnection ic = mInputConnection.get(); 226 if (ic == null || !isActive()) { 227 Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); 228 args.callback.setTextBeforeCursor(null, args.seq); 229 return; 230 } 231 args.callback.setTextBeforeCursor(ic.getTextBeforeCursor( 232 msg.arg1, msg.arg2), args.seq); 233 } catch (RemoteException e) { 234 Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e); 235 } 236 return; 237 } 238 case DO_GET_SELECTED_TEXT: { 239 SomeArgs args = (SomeArgs)msg.obj; 240 try { 241 InputConnection ic = mInputConnection.get(); 242 if (ic == null || !isActive()) { 243 Log.w(TAG, "getSelectedText on inactive InputConnection"); 244 args.callback.setSelectedText(null, args.seq); 245 return; 246 } 247 args.callback.setSelectedText(ic.getSelectedText( 248 msg.arg1), args.seq); 249 } catch (RemoteException e) { 250 Log.w(TAG, "Got RemoteException calling setSelectedText", e); 251 } 252 return; 253 } 254 case DO_GET_CURSOR_CAPS_MODE: { 255 SomeArgs args = (SomeArgs)msg.obj; 256 try { 257 InputConnection ic = mInputConnection.get(); 258 if (ic == null || !isActive()) { 259 Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); 260 args.callback.setCursorCapsMode(0, args.seq); 261 return; 262 } 263 args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1), 264 args.seq); 265 } catch (RemoteException e) { 266 Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e); 267 } 268 return; 269 } 270 case DO_GET_EXTRACTED_TEXT: { 271 SomeArgs args = (SomeArgs)msg.obj; 272 try { 273 InputConnection ic = mInputConnection.get(); 274 if (ic == null || !isActive()) { 275 Log.w(TAG, "getExtractedText on inactive InputConnection"); 276 args.callback.setExtractedText(null, args.seq); 277 return; 278 } 279 args.callback.setExtractedText(ic.getExtractedText( 280 (ExtractedTextRequest)args.arg1, msg.arg1), args.seq); 281 } catch (RemoteException e) { 282 Log.w(TAG, "Got RemoteException calling setExtractedText", e); 283 } 284 return; 285 } 286 case DO_COMMIT_TEXT: { 287 InputConnection ic = mInputConnection.get(); 288 if (ic == null || !isActive()) { 289 Log.w(TAG, "commitText on inactive InputConnection"); 290 return; 291 } 292 ic.commitText((CharSequence)msg.obj, msg.arg1); 293 return; 294 } 295 case DO_SET_SELECTION: { 296 InputConnection ic = mInputConnection.get(); 297 if (ic == null || !isActive()) { 298 Log.w(TAG, "setSelection on inactive InputConnection"); 299 return; 300 } 301 ic.setSelection(msg.arg1, msg.arg2); 302 return; 303 } 304 case DO_PERFORM_EDITOR_ACTION: { 305 InputConnection ic = mInputConnection.get(); 306 if (ic == null || !isActive()) { 307 Log.w(TAG, "performEditorAction on inactive InputConnection"); 308 return; 309 } 310 ic.performEditorAction(msg.arg1); 311 return; 312 } 313 case DO_PERFORM_CONTEXT_MENU_ACTION: { 314 InputConnection ic = mInputConnection.get(); 315 if (ic == null || !isActive()) { 316 Log.w(TAG, "performContextMenuAction on inactive InputConnection"); 317 return; 318 } 319 ic.performContextMenuAction(msg.arg1); 320 return; 321 } 322 case DO_COMMIT_COMPLETION: { 323 InputConnection ic = mInputConnection.get(); 324 if (ic == null || !isActive()) { 325 Log.w(TAG, "commitCompletion on inactive InputConnection"); 326 return; 327 } 328 ic.commitCompletion((CompletionInfo)msg.obj); 329 return; 330 } 331 case DO_COMMIT_CORRECTION: { 332 InputConnection ic = mInputConnection.get(); 333 if (ic == null || !isActive()) { 334 Log.w(TAG, "commitCorrection on inactive InputConnection"); 335 return; 336 } 337 ic.commitCorrection((CorrectionInfo)msg.obj); 338 return; 339 } 340 case DO_SET_COMPOSING_TEXT: { 341 InputConnection ic = mInputConnection.get(); 342 if (ic == null || !isActive()) { 343 Log.w(TAG, "setComposingText on inactive InputConnection"); 344 return; 345 } 346 ic.setComposingText((CharSequence)msg.obj, msg.arg1); 347 return; 348 } 349 case DO_SET_COMPOSING_REGION: { 350 InputConnection ic = mInputConnection.get(); 351 if (ic == null || !isActive()) { 352 Log.w(TAG, "setComposingRegion on inactive InputConnection"); 353 return; 354 } 355 ic.setComposingRegion(msg.arg1, msg.arg2); 356 return; 357 } 358 case DO_FINISH_COMPOSING_TEXT: { 359 InputConnection ic = mInputConnection.get(); 360 // Note we do NOT check isActive() here, because this is safe 361 // for an IME to call at any time, and we need to allow it 362 // through to clean up our state after the IME has switched to 363 // another client. 364 if (ic == null) { 365 Log.w(TAG, "finishComposingText on inactive InputConnection"); 366 return; 367 } 368 ic.finishComposingText(); 369 return; 370 } 371 case DO_SEND_KEY_EVENT: { 372 InputConnection ic = mInputConnection.get(); 373 if (ic == null || !isActive()) { 374 Log.w(TAG, "sendKeyEvent on inactive InputConnection"); 375 return; 376 } 377 ic.sendKeyEvent((KeyEvent)msg.obj); 378 return; 379 } 380 case DO_CLEAR_META_KEY_STATES: { 381 InputConnection ic = mInputConnection.get(); 382 if (ic == null || !isActive()) { 383 Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); 384 return; 385 } 386 ic.clearMetaKeyStates(msg.arg1); 387 return; 388 } 389 case DO_DELETE_SURROUNDING_TEXT: { 390 InputConnection ic = mInputConnection.get(); 391 if (ic == null || !isActive()) { 392 Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); 393 return; 394 } 395 ic.deleteSurroundingText(msg.arg1, msg.arg2); 396 return; 397 } 398 case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: { 399 InputConnection ic = mInputConnection.get(); 400 if (ic == null || !isActive()) { 401 Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); 402 return; 403 } 404 ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); 405 return; 406 } 407 case DO_BEGIN_BATCH_EDIT: { 408 InputConnection ic = mInputConnection.get(); 409 if (ic == null || !isActive()) { 410 Log.w(TAG, "beginBatchEdit on inactive InputConnection"); 411 return; 412 } 413 ic.beginBatchEdit(); 414 return; 415 } 416 case DO_END_BATCH_EDIT: { 417 InputConnection ic = mInputConnection.get(); 418 if (ic == null || !isActive()) { 419 Log.w(TAG, "endBatchEdit on inactive InputConnection"); 420 return; 421 } 422 ic.endBatchEdit(); 423 return; 424 } 425 case DO_REPORT_FULLSCREEN_MODE: { 426 InputConnection ic = mInputConnection.get(); 427 if (ic == null) { 428 Log.w(TAG, "reportFullscreenMode on inexistent InputConnection"); 429 return; 430 } 431 ic.reportFullscreenMode(msg.arg1 == 1); 432 return; 433 } 434 case DO_PERFORM_PRIVATE_COMMAND: { 435 InputConnection ic = mInputConnection.get(); 436 if (ic == null || !isActive()) { 437 Log.w(TAG, "performPrivateCommand on inactive InputConnection"); 438 return; 439 } 440 SomeArgs args = (SomeArgs)msg.obj; 441 ic.performPrivateCommand((String)args.arg1, 442 (Bundle)args.arg2); 443 return; 444 } 445 case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { 446 SomeArgs args = (SomeArgs)msg.obj; 447 try { 448 InputConnection ic = mInputConnection.get(); 449 if (ic == null || !isActive()) { 450 Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); 451 args.callback.setRequestUpdateCursorAnchorInfoResult(false, args.seq); 452 return; 453 } 454 args.callback.setRequestUpdateCursorAnchorInfoResult( 455 ic.requestCursorUpdates(msg.arg1), args.seq); 456 } catch (RemoteException e) { 457 Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e); 458 } 459 return; 460 } 461 } 462 Log.w(TAG, "Unhandled message code: " + msg.what); 463 } 464 465 Message obtainMessage(int what) { 466 return mH.obtainMessage(what); 467 } 468 469 Message obtainMessageII(int what, int arg1, int arg2) { 470 return mH.obtainMessage(what, arg1, arg2); 471 } 472 473 Message obtainMessageO(int what, Object arg1) { 474 return mH.obtainMessage(what, 0, 0, arg1); 475 } 476 477 Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) { 478 SomeArgs args = new SomeArgs(); 479 args.callback = callback; 480 args.seq = seq; 481 return mH.obtainMessage(what, arg1, 0, args); 482 } 483 484 Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) { 485 SomeArgs args = new SomeArgs(); 486 args.callback = callback; 487 args.seq = seq; 488 return mH.obtainMessage(what, arg1, arg2, args); 489 } 490 491 Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) { 492 SomeArgs args = new SomeArgs(); 493 args.arg1 = arg1; 494 args.callback = callback; 495 args.seq = seq; 496 return mH.obtainMessage(what, 0, 0, args); 497 } 498 499 Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq, 500 IInputContextCallback callback) { 501 SomeArgs args = new SomeArgs(); 502 args.arg1 = arg2; 503 args.callback = callback; 504 args.seq = seq; 505 return mH.obtainMessage(what, arg1, 0, args); 506 } 507 508 Message obtainMessageIO(int what, int arg1, Object arg2) { 509 return mH.obtainMessage(what, arg1, 0, arg2); 510 } 511 512 Message obtainMessageOO(int what, Object arg1, Object arg2) { 513 SomeArgs args = new SomeArgs(); 514 args.arg1 = arg1; 515 args.arg2 = arg2; 516 return mH.obtainMessage(what, 0, 0, args); 517 } 518} 519