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