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