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