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