1 /* 2 * Copyright (C) 2017 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.cts.mockime; 18 19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 20 21 import static org.junit.Assume.assumeFalse; 22 23 import android.app.UiAutomation; 24 import android.content.BroadcastReceiver; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.os.ParcelFileDescriptor; 33 import android.os.SystemClock; 34 import android.provider.Settings; 35 import android.text.TextUtils; 36 import android.view.KeyEvent; 37 import android.view.inputmethod.CompletionInfo; 38 import android.view.inputmethod.CorrectionInfo; 39 import android.view.inputmethod.ExtractedTextRequest; 40 import android.view.inputmethod.InputConnection; 41 import android.view.inputmethod.InputContentInfo; 42 import android.view.inputmethod.InputMethodManager; 43 import android.view.inputmethod.InputMethodSystemProperty; 44 45 import androidx.annotation.GuardedBy; 46 import androidx.annotation.NonNull; 47 import androidx.annotation.Nullable; 48 49 import com.android.compatibility.common.util.PollingCheck; 50 51 import java.io.IOException; 52 import java.util.concurrent.TimeUnit; 53 54 /** 55 * Represents an active Mock IME session, which provides basic primitives to write end-to-end tests 56 * for IME APIs. 57 * 58 * <p>To use {@link MockIme} via {@link MockImeSession}, you need to </p> 59 * <p>Public methods are not thread-safe.</p> 60 */ 61 public class MockImeSession implements AutoCloseable { 62 private final String mImeEventActionName = 63 "com.android.cts.mockime.action.IME_EVENT." + SystemClock.elapsedRealtimeNanos(); 64 65 private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10); 66 67 @NonNull 68 private final Context mContext; 69 @NonNull 70 private final UiAutomation mUiAutomation; 71 72 private final HandlerThread mHandlerThread = new HandlerThread("EventReceiver"); 73 74 private static final class EventStore { 75 private static final int INITIAL_ARRAY_SIZE = 32; 76 77 @NonNull 78 public final ImeEvent[] mArray; 79 public int mLength; 80 EventStore()81 EventStore() { 82 mArray = new ImeEvent[INITIAL_ARRAY_SIZE]; 83 mLength = 0; 84 } 85 EventStore(EventStore src, int newLength)86 EventStore(EventStore src, int newLength) { 87 mArray = new ImeEvent[newLength]; 88 mLength = src.mLength; 89 System.arraycopy(src.mArray, 0, mArray, 0, src.mLength); 90 } 91 add(ImeEvent event)92 public EventStore add(ImeEvent event) { 93 if (mLength + 1 <= mArray.length) { 94 mArray[mLength] = event; 95 ++mLength; 96 return this; 97 } else { 98 return new EventStore(this, mLength * 2).add(event); 99 } 100 } 101 takeSnapshot()102 public ImeEventStream.ImeEventArray takeSnapshot() { 103 return new ImeEventStream.ImeEventArray(mArray, mLength); 104 } 105 } 106 107 private static final class MockImeEventReceiver extends BroadcastReceiver { 108 private final Object mLock = new Object(); 109 110 @GuardedBy("mLock") 111 @NonNull 112 private EventStore mCurrentEventStore = new EventStore(); 113 114 @NonNull 115 private final String mActionName; 116 MockImeEventReceiver(@onNull String actionName)117 MockImeEventReceiver(@NonNull String actionName) { 118 mActionName = actionName; 119 } 120 121 @Override onReceive(Context context, Intent intent)122 public void onReceive(Context context, Intent intent) { 123 if (TextUtils.equals(mActionName, intent.getAction())) { 124 synchronized (mLock) { 125 mCurrentEventStore = 126 mCurrentEventStore.add(ImeEvent.fromBundle(intent.getExtras())); 127 } 128 } 129 } 130 takeEventSnapshot()131 public ImeEventStream.ImeEventArray takeEventSnapshot() { 132 synchronized (mLock) { 133 return mCurrentEventStore.takeSnapshot(); 134 } 135 } 136 } 137 private final MockImeEventReceiver mEventReceiver = 138 new MockImeEventReceiver(mImeEventActionName); 139 140 private final ImeEventStream mEventStream = 141 new ImeEventStream(mEventReceiver::takeEventSnapshot); 142 executeShellCommand( @onNull UiAutomation uiAutomation, @NonNull String command)143 private static String executeShellCommand( 144 @NonNull UiAutomation uiAutomation, @NonNull String command) throws IOException { 145 try (ParcelFileDescriptor.AutoCloseInputStream in = 146 new ParcelFileDescriptor.AutoCloseInputStream( 147 uiAutomation.executeShellCommand(command))) { 148 final StringBuilder sb = new StringBuilder(); 149 final byte[] buffer = new byte[4096]; 150 while (true) { 151 final int numRead = in.read(buffer); 152 if (numRead <= 0) { 153 break; 154 } 155 sb.append(new String(buffer, 0, numRead)); 156 } 157 return sb.toString(); 158 } 159 } 160 161 @Nullable getCurrentInputMethodId()162 private String getCurrentInputMethodId() { 163 // TODO: Replace this with IMM#getCurrentInputMethodIdForTesting() 164 return Settings.Secure.getString(mContext.getContentResolver(), 165 Settings.Secure.DEFAULT_INPUT_METHOD); 166 } 167 168 @Nullable writeMockImeSettings(@onNull Context context, @NonNull String imeEventActionName, @Nullable ImeSettings.Builder imeSettings)169 private static void writeMockImeSettings(@NonNull Context context, 170 @NonNull String imeEventActionName, 171 @Nullable ImeSettings.Builder imeSettings) throws Exception { 172 final Bundle bundle = ImeSettings.serializeToBundle(imeEventActionName, imeSettings); 173 context.getContentResolver().call(SettingsProvider.AUTHORITY, "write", null, bundle); 174 } 175 getMockImeComponentName()176 private ComponentName getMockImeComponentName() { 177 return MockIme.getComponentName(); 178 } 179 getMockImeId()180 private String getMockImeId() { 181 return MockIme.getImeId(); 182 } 183 MockImeSession(@onNull Context context, @NonNull UiAutomation uiAutomation)184 private MockImeSession(@NonNull Context context, @NonNull UiAutomation uiAutomation) { 185 mContext = context; 186 mUiAutomation = uiAutomation; 187 } 188 initialize(@ullable ImeSettings.Builder imeSettings)189 private void initialize(@Nullable ImeSettings.Builder imeSettings) throws Exception { 190 // Make sure that MockIME is not selected. 191 if (mContext.getSystemService(InputMethodManager.class) 192 .getInputMethodList() 193 .stream() 194 .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) { 195 executeShellCommand(mUiAutomation, "ime reset"); 196 } 197 if (mContext.getSystemService(InputMethodManager.class) 198 .getEnabledInputMethodList() 199 .stream() 200 .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) { 201 throw new IllegalStateException(); 202 } 203 204 writeMockImeSettings(mContext, mImeEventActionName, imeSettings); 205 206 mHandlerThread.start(); 207 mContext.registerReceiver(mEventReceiver, 208 new IntentFilter(mImeEventActionName), null /* broadcastPermission */, 209 new Handler(mHandlerThread.getLooper())); 210 211 executeShellCommand(mUiAutomation, "ime enable " + getMockImeId()); 212 executeShellCommand(mUiAutomation, "ime set " + getMockImeId()); 213 214 PollingCheck.check("Make sure that MockIME becomes available", TIMEOUT, 215 () -> getMockImeId().equals(getCurrentInputMethodId())); 216 } 217 218 /** @see #create(Context, UiAutomation, ImeSettings.Builder) */ 219 @NonNull create(@onNull Context context)220 public static MockImeSession create(@NonNull Context context) throws Exception { 221 return create(context, getInstrumentation().getUiAutomation(), new ImeSettings.Builder()); 222 } 223 224 /** 225 * Creates a new Mock IME session. During this session, you can receive various events from 226 * {@link MockIme}. 227 * 228 * @param context {@link Context} to be used to receive inter-process events from the 229 * {@link MockIme} (e.g. via {@link BroadcastReceiver} 230 * @param uiAutomation {@link UiAutomation} object to change the device state that are typically 231 * guarded by permissions. 232 * @param imeSettings Key-value pairs to be passed to the {@link MockIme}. 233 * @return A session object, with which you can retrieve event logs from the {@link MockIme} and 234 * can clean up the session. 235 */ 236 @NonNull create( @onNull Context context, @NonNull UiAutomation uiAutomation, @Nullable ImeSettings.Builder imeSettings)237 public static MockImeSession create( 238 @NonNull Context context, 239 @NonNull UiAutomation uiAutomation, 240 @Nullable ImeSettings.Builder imeSettings) throws Exception { 241 // Currently, MockIme doesn't fully support multi-client IME. Skip tests until it does. 242 // TODO: Re-enable when MockIme supports multi-client IME. 243 assumeFalse("MockIme session doesn't support Multi-Client IME, skip it", 244 InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED); 245 246 final MockImeSession client = new MockImeSession(context, uiAutomation); 247 client.initialize(imeSettings); 248 return client; 249 } 250 251 /** 252 * @return {@link ImeEventStream} object that stores events sent from {@link MockIme} since the 253 * session is created. 254 */ openEventStream()255 public ImeEventStream openEventStream() { 256 return mEventStream.copy(); 257 } 258 259 /** 260 * Closes the active session and de-selects {@link MockIme}. Currently which IME will be 261 * selected next is up to the system. 262 */ close()263 public void close() throws Exception { 264 executeShellCommand(mUiAutomation, "ime reset"); 265 266 PollingCheck.check("Make sure that MockIME becomes unavailable", TIMEOUT, () -> 267 mContext.getSystemService(InputMethodManager.class) 268 .getEnabledInputMethodList() 269 .stream() 270 .noneMatch(info -> getMockImeComponentName().equals(info.getComponent()))); 271 272 mContext.unregisterReceiver(mEventReceiver); 273 mHandlerThread.quitSafely(); 274 mContext.getContentResolver().call(SettingsProvider.AUTHORITY, "delete", null, null); 275 } 276 277 /** 278 * Common logic to send a special command to {@link MockIme}. 279 * 280 * @param commandName command to be passed to {@link MockIme} 281 * @param params {@link Bundle} to be passed to {@link MockIme} as a parameter set of 282 * {@code commandName} 283 * @return {@link ImeCommand} that is sent to {@link MockIme}. It can be passed to 284 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 285 * wait until this event is handled by {@link MockIme}. 286 */ 287 @NonNull callCommandInternal(@onNull String commandName, @NonNull Bundle params)288 private ImeCommand callCommandInternal(@NonNull String commandName, @NonNull Bundle params) { 289 final ImeCommand command = new ImeCommand( 290 commandName, SystemClock.elapsedRealtimeNanos(), true, params); 291 final Intent intent = new Intent(); 292 intent.setPackage(MockIme.getComponentName().getPackageName()); 293 intent.setAction(MockIme.getCommandActionName(mImeEventActionName)); 294 intent.putExtras(command.toBundle()); 295 mContext.sendBroadcast(intent); 296 return command; 297 } 298 299 /** 300 * Lets {@link MockIme} to call {@link InputConnection#getTextBeforeCursor(int, int)} with the 301 * given parameters. 302 * 303 * <p>This triggers {@code getCurrentInputConnection().getTextBeforeCursor(n, flag)}.</p> 304 * 305 * <p>Use {@link ImeEvent#getReturnCharSequenceValue()} for {@link ImeEvent} returned from 306 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 307 * value returned from the API.</p> 308 * 309 * @param n to be passed as the {@code n} parameter. 310 * @param flag to be passed as the {@code flag} parameter. 311 * @return {@link ImeCommand} object that can be passed to 312 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 313 * wait until this event is handled by {@link MockIme}. 314 */ 315 @NonNull callGetTextBeforeCursor(int n, int flag)316 public ImeCommand callGetTextBeforeCursor(int n, int flag) { 317 final Bundle params = new Bundle(); 318 params.putInt("n", n); 319 params.putInt("flag", flag); 320 return callCommandInternal("getTextBeforeCursor", params); 321 } 322 323 /** 324 * Lets {@link MockIme} to call {@link InputConnection#getTextAfterCursor(int, int)} with the 325 * given parameters. 326 * 327 * <p>This triggers {@code getCurrentInputConnection().getTextAfterCursor(n, flag)}.</p> 328 * 329 * <p>Use {@link ImeEvent#getReturnCharSequenceValue()} for {@link ImeEvent} returned from 330 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 331 * value returned from the API.</p> 332 * 333 * @param n to be passed as the {@code n} parameter. 334 * @param flag to be passed as the {@code flag} parameter. 335 * @return {@link ImeCommand} object that can be passed to 336 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 337 * wait until this event is handled by {@link MockIme}. 338 */ 339 @NonNull callGetTextAfterCursor(int n, int flag)340 public ImeCommand callGetTextAfterCursor(int n, int flag) { 341 final Bundle params = new Bundle(); 342 params.putInt("n", n); 343 params.putInt("flag", flag); 344 return callCommandInternal("getTextAfterCursor", params); 345 } 346 347 /** 348 * Lets {@link MockIme} to call {@link InputConnection#getSelectedText(int)} with the 349 * given parameters. 350 * 351 * <p>This triggers {@code getCurrentInputConnection().getSelectedText(flag)}.</p> 352 * 353 * <p>Use {@link ImeEvent#getReturnCharSequenceValue()} for {@link ImeEvent} returned from 354 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 355 * value returned from the API.</p> 356 * 357 * @param flag to be passed as the {@code flag} parameter. 358 * @return {@link ImeCommand} object that can be passed to 359 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 360 * wait until this event is handled by {@link MockIme}. 361 */ 362 @NonNull callGetSelectedText(int flag)363 public ImeCommand callGetSelectedText(int flag) { 364 final Bundle params = new Bundle(); 365 params.putInt("flag", flag); 366 return callCommandInternal("getSelectedText", params); 367 } 368 369 /** 370 * Lets {@link MockIme} to call {@link InputConnection#getCursorCapsMode(int)} with the given 371 * parameters. 372 * 373 * <p>This triggers {@code getCurrentInputConnection().getCursorCapsMode(reqModes)}.</p> 374 * 375 * <p>Use {@link ImeEvent#getReturnIntegerValue()} for {@link ImeEvent} returned from 376 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 377 * value returned from the API.</p> 378 * 379 * @param reqModes to be passed as the {@code reqModes} parameter. 380 * @return {@link ImeCommand} object that can be passed to 381 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 382 * wait until this event is handled by {@link MockIme}. 383 */ 384 @NonNull callGetCursorCapsMode(int reqModes)385 public ImeCommand callGetCursorCapsMode(int reqModes) { 386 final Bundle params = new Bundle(); 387 params.putInt("reqModes", reqModes); 388 return callCommandInternal("getCursorCapsMode", params); 389 } 390 391 /** 392 * Lets {@link MockIme} to call 393 * {@link InputConnection#getExtractedText(ExtractedTextRequest, int)} with the given 394 * parameters. 395 * 396 * <p>This triggers {@code getCurrentInputConnection().getExtractedText(request, flags)}.</p> 397 * 398 * <p>Use {@link ImeEvent#getReturnParcelableValue()} for {@link ImeEvent} returned from 399 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 400 * value returned from the API.</p> 401 * 402 * @param request to be passed as the {@code request} parameter 403 * @param flags to be passed as the {@code flags} parameter 404 * @return {@link ImeCommand} object that can be passed to 405 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 406 * wait until this event is handled by {@link MockIme}. 407 */ 408 @NonNull callGetExtractedText(@ullable ExtractedTextRequest request, int flags)409 public ImeCommand callGetExtractedText(@Nullable ExtractedTextRequest request, int flags) { 410 final Bundle params = new Bundle(); 411 params.putParcelable("request", request); 412 params.putInt("flags", flags); 413 return callCommandInternal("getExtractedText", params); 414 } 415 416 /** 417 * Lets {@link MockIme} to call {@link InputConnection#deleteSurroundingText(int, int)} with the 418 * given parameters. 419 * 420 * <p>This triggers 421 * {@code getCurrentInputConnection().deleteSurroundingText(beforeLength, afterLength)}.</p> 422 * 423 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 424 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 425 * value returned from the API.</p> 426 * 427 * @param beforeLength to be passed as the {@code beforeLength} parameter 428 * @param afterLength to be passed as the {@code afterLength} parameter 429 * @return {@link ImeCommand} object that can be passed to 430 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 431 * wait until this event is handled by {@link MockIme}. 432 */ 433 @NonNull callDeleteSurroundingText(int beforeLength, int afterLength)434 public ImeCommand callDeleteSurroundingText(int beforeLength, int afterLength) { 435 final Bundle params = new Bundle(); 436 params.putInt("beforeLength", beforeLength); 437 params.putInt("afterLength", afterLength); 438 return callCommandInternal("deleteSurroundingText", params); 439 } 440 441 /** 442 * Lets {@link MockIme} to call 443 * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)} with the given 444 * parameters. 445 * 446 * <p>This triggers {@code getCurrentInputConnection().deleteSurroundingTextInCodePoints( 447 * beforeLength, afterLength)}.</p> 448 * 449 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 450 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 451 * value returned from the API.</p> 452 * 453 * @param beforeLength to be passed as the {@code beforeLength} parameter 454 * @param afterLength to be passed as the {@code afterLength} parameter 455 * @return {@link ImeCommand} object that can be passed to 456 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 457 * wait until this event is handled by {@link MockIme}. 458 */ 459 @NonNull callDeleteSurroundingTextInCodePoints(int beforeLength, int afterLength)460 public ImeCommand callDeleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { 461 final Bundle params = new Bundle(); 462 params.putInt("beforeLength", beforeLength); 463 params.putInt("afterLength", afterLength); 464 return callCommandInternal("deleteSurroundingTextInCodePoints", params); 465 } 466 467 /** 468 * Lets {@link MockIme} to call {@link InputConnection#setComposingText(CharSequence, int)} with 469 * the given parameters. 470 * 471 * <p>This triggers 472 * {@code getCurrentInputConnection().setComposingText(text, newCursorPosition)}.</p> 473 * 474 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 475 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 476 * value returned from the API.</p> 477 * 478 * @param text to be passed as the {@code text} parameter 479 * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter 480 * @return {@link ImeCommand} object that can be passed to 481 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 482 * wait until this event is handled by {@link MockIme}. 483 */ 484 @NonNull callSetComposingText(@ullable CharSequence text, int newCursorPosition)485 public ImeCommand callSetComposingText(@Nullable CharSequence text, int newCursorPosition) { 486 final Bundle params = new Bundle(); 487 params.putCharSequence("text", text); 488 params.putInt("newCursorPosition", newCursorPosition); 489 return callCommandInternal("setComposingText", params); 490 } 491 492 /** 493 * Lets {@link MockIme} to call {@link InputConnection#setComposingRegion(int, int)} with the 494 * given parameters. 495 * 496 * <p>This triggers {@code getCurrentInputConnection().setComposingRegion(start, end)}.</p> 497 * 498 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 499 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 500 * value returned from the API.</p> 501 * 502 * @param start to be passed as the {@code start} parameter 503 * @param end to be passed as the {@code end} parameter 504 * @return {@link ImeCommand} object that can be passed to 505 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 506 * wait until this event is handled by {@link MockIme}. 507 */ 508 @NonNull callSetComposingRegion(int start, int end)509 public ImeCommand callSetComposingRegion(int start, int end) { 510 final Bundle params = new Bundle(); 511 params.putInt("start", start); 512 params.putInt("end", end); 513 return callCommandInternal("setComposingRegion", params); 514 } 515 516 /** 517 * Lets {@link MockIme} to call {@link InputConnection#finishComposingText()} with the given 518 * parameters. 519 * 520 * <p>This triggers {@code getCurrentInputConnection().finishComposingText()}.</p> 521 * 522 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 523 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 524 * value returned from the API.</p> 525 * 526 * @return {@link ImeCommand} object that can be passed to 527 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 528 * wait until this event is handled by {@link MockIme}. 529 */ 530 @NonNull callFinishComposingText()531 public ImeCommand callFinishComposingText() { 532 final Bundle params = new Bundle(); 533 return callCommandInternal("finishComposingText", params); 534 } 535 536 /** 537 * Lets {@link MockIme} to call {@link InputConnection#commitText(CharSequence, int)} with the 538 * given parameters. 539 * 540 * <p>This triggers {@code getCurrentInputConnection().commitText(text, newCursorPosition)}.</p> 541 * 542 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 543 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 544 * value returned from the API.</p> 545 * 546 * @param text to be passed as the {@code text} parameter 547 * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter 548 * @return {@link ImeCommand} object that can be passed to 549 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 550 * wait until this event is handled by {@link MockIme} 551 */ 552 @NonNull callCommitText(@ullable CharSequence text, int newCursorPosition)553 public ImeCommand callCommitText(@Nullable CharSequence text, int newCursorPosition) { 554 final Bundle params = new Bundle(); 555 params.putCharSequence("text", text); 556 params.putInt("newCursorPosition", newCursorPosition); 557 return callCommandInternal("commitText", params); 558 } 559 560 /** 561 * Lets {@link MockIme} to call {@link InputConnection#commitCompletion(CompletionInfo)} with 562 * the given parameters. 563 * 564 * <p>This triggers {@code getCurrentInputConnection().commitCompletion(text)}.</p> 565 * 566 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 567 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 568 * value returned from the API.</p> 569 * 570 * @param text to be passed as the {@code text} parameter 571 * @return {@link ImeCommand} object that can be passed to 572 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 573 * wait until this event is handled by {@link MockIme} 574 */ 575 @NonNull callCommitCompletion(@ullable CompletionInfo text)576 public ImeCommand callCommitCompletion(@Nullable CompletionInfo text) { 577 final Bundle params = new Bundle(); 578 params.putParcelable("text", text); 579 return callCommandInternal("commitCompletion", params); 580 } 581 582 /** 583 * Lets {@link MockIme} to call {@link InputConnection#commitCorrection(CorrectionInfo)} with 584 * the given parameters. 585 * 586 * <p>This triggers {@code getCurrentInputConnection().commitCorrection(correctionInfo)}.</p> 587 * 588 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 589 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 590 * value returned from the API.</p> 591 * 592 * @param correctionInfo to be passed as the {@code correctionInfo} parameter 593 * @return {@link ImeCommand} object that can be passed to 594 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 595 * wait until this event is handled by {@link MockIme} 596 */ 597 @NonNull callCommitCorrection(@ullable CorrectionInfo correctionInfo)598 public ImeCommand callCommitCorrection(@Nullable CorrectionInfo correctionInfo) { 599 final Bundle params = new Bundle(); 600 params.putParcelable("correctionInfo", correctionInfo); 601 return callCommandInternal("commitCorrection", params); 602 } 603 604 /** 605 * Lets {@link MockIme} to call {@link InputConnection#setSelection(int, int)} with the given 606 * parameters. 607 * 608 * <p>This triggers {@code getCurrentInputConnection().setSelection(start, end)}.</p> 609 * 610 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 611 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 612 * value returned from the API.</p> 613 * 614 * @param start to be passed as the {@code start} parameter 615 * @param end to be passed as the {@code end} parameter 616 * @return {@link ImeCommand} object that can be passed to 617 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 618 * wait until this event is handled by {@link MockIme} 619 */ 620 @NonNull callSetSelection(int start, int end)621 public ImeCommand callSetSelection(int start, int end) { 622 final Bundle params = new Bundle(); 623 params.putInt("start", start); 624 params.putInt("end", end); 625 return callCommandInternal("setSelection", params); 626 } 627 628 /** 629 * Lets {@link MockIme} to call {@link InputConnection#performEditorAction(int)} with the given 630 * parameters. 631 * 632 * <p>This triggers {@code getCurrentInputConnection().performEditorAction(editorAction)}.</p> 633 * 634 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 635 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 636 * value returned from the API.</p> 637 * 638 * @param editorAction to be passed as the {@code editorAction} parameter 639 * @return {@link ImeCommand} object that can be passed to 640 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 641 * wait until this event is handled by {@link MockIme} 642 */ 643 @NonNull callPerformEditorAction(int editorAction)644 public ImeCommand callPerformEditorAction(int editorAction) { 645 final Bundle params = new Bundle(); 646 params.putInt("editorAction", editorAction); 647 return callCommandInternal("performEditorAction", params); 648 } 649 650 /** 651 * Lets {@link MockIme} to call {@link InputConnection#performContextMenuAction(int)} with the 652 * given parameters. 653 * 654 * <p>This triggers {@code getCurrentInputConnection().performContextMenuAction(id)}.</p> 655 * 656 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 657 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 658 * value returned from the API.</p> 659 * 660 * @param id to be passed as the {@code id} parameter 661 * @return {@link ImeCommand} object that can be passed to 662 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 663 * wait until this event is handled by {@link MockIme} 664 */ 665 @NonNull callPerformContextMenuAction(int id)666 public ImeCommand callPerformContextMenuAction(int id) { 667 final Bundle params = new Bundle(); 668 params.putInt("id", id); 669 return callCommandInternal("performContextMenuAction", params); 670 } 671 672 /** 673 * Lets {@link MockIme} to call {@link InputConnection#beginBatchEdit()} with the given 674 * parameters. 675 * 676 * <p>This triggers {@code getCurrentInputConnection().beginBatchEdit()}.</p> 677 * 678 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 679 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 680 * value returned from the API.</p> 681 * 682 * @return {@link ImeCommand} object that can be passed to 683 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 684 * wait until this event is handled by {@link MockIme} 685 */ 686 @NonNull callBeginBatchEdit()687 public ImeCommand callBeginBatchEdit() { 688 final Bundle params = new Bundle(); 689 return callCommandInternal("beginBatchEdit", params); 690 } 691 692 /** 693 * Lets {@link MockIme} to call {@link InputConnection#endBatchEdit()} with the given 694 * parameters. 695 * 696 * <p>This triggers {@code getCurrentInputConnection().endBatchEdit()}.</p> 697 * 698 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 699 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 700 * value returned from the API.</p> 701 * 702 * @return {@link ImeCommand} object that can be passed to 703 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 704 * wait until this event is handled by {@link MockIme} 705 */ 706 @NonNull callEndBatchEdit()707 public ImeCommand callEndBatchEdit() { 708 final Bundle params = new Bundle(); 709 return callCommandInternal("endBatchEdit", params); 710 } 711 712 /** 713 * Lets {@link MockIme} to call {@link InputConnection#sendKeyEvent(KeyEvent)} with the given 714 * parameters. 715 * 716 * <p>This triggers {@code getCurrentInputConnection().sendKeyEvent(event)}.</p> 717 * 718 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 719 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 720 * value returned from the API.</p> 721 * 722 * @param event to be passed as the {@code event} parameter 723 * @return {@link ImeCommand} object that can be passed to 724 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 725 * wait until this event is handled by {@link MockIme} 726 */ 727 @NonNull callSendKeyEvent(@ullable KeyEvent event)728 public ImeCommand callSendKeyEvent(@Nullable KeyEvent event) { 729 final Bundle params = new Bundle(); 730 params.putParcelable("event", event); 731 return callCommandInternal("sendKeyEvent", params); 732 } 733 734 /** 735 * Lets {@link MockIme} to call {@link InputConnection#clearMetaKeyStates(int)} with the given 736 * parameters. 737 * 738 * <p>This triggers {@code getCurrentInputConnection().sendKeyEvent(event)}.</p> 739 * 740 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 741 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 742 * value returned from the API.</p> 743 * 744 * @param states to be passed as the {@code states} parameter 745 * @return {@link ImeCommand} object that can be passed to 746 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 747 * wait until this event is handled by {@link MockIme} 748 */ 749 @NonNull callClearMetaKeyStates(int states)750 public ImeCommand callClearMetaKeyStates(int states) { 751 final Bundle params = new Bundle(); 752 params.putInt("states", states); 753 return callCommandInternal("clearMetaKeyStates", params); 754 } 755 756 /** 757 * Lets {@link MockIme} to call {@link InputConnection#reportFullscreenMode(boolean)} with the 758 * given parameters. 759 * 760 * <p>This triggers {@code getCurrentInputConnection().reportFullscreenMode(enabled)}.</p> 761 * 762 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 763 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 764 * value returned from the API.</p> 765 * 766 * @param enabled to be passed as the {@code enabled} parameter 767 * @return {@link ImeCommand} object that can be passed to 768 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 769 * wait until this event is handled by {@link MockIme} 770 */ 771 @NonNull callReportFullscreenMode(boolean enabled)772 public ImeCommand callReportFullscreenMode(boolean enabled) { 773 final Bundle params = new Bundle(); 774 params.putBoolean("enabled", enabled); 775 return callCommandInternal("reportFullscreenMode", params); 776 } 777 778 /** 779 * Lets {@link MockIme} to call {@link InputConnection#performPrivateCommand(String, Bundle)} 780 * with the given parameters. 781 * 782 * <p>This triggers {@code getCurrentInputConnection().performPrivateCommand(action, data)}.</p> 783 * 784 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 785 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 786 * value returned from the API.</p> 787 * 788 * @param action to be passed as the {@code action} parameter 789 * @param data to be passed as the {@code data} parameter 790 * @return {@link ImeCommand} object that can be passed to 791 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 792 * wait until this event is handled by {@link MockIme} 793 */ 794 @NonNull callPerformPrivateCommand(@ullable String action, Bundle data)795 public ImeCommand callPerformPrivateCommand(@Nullable String action, Bundle data) { 796 final Bundle params = new Bundle(); 797 params.putString("action", action); 798 params.putBundle("data", data); 799 return callCommandInternal("performPrivateCommand", params); 800 } 801 802 /** 803 * Lets {@link MockIme} to call {@link InputConnection#requestCursorUpdates(int)} with the given 804 * parameters. 805 * 806 * <p>This triggers {@code getCurrentInputConnection().requestCursorUpdates(cursorUpdateMode)}. 807 * </p> 808 * 809 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 810 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 811 * value returned from the API.</p> 812 * 813 * @param cursorUpdateMode to be passed as the {@code cursorUpdateMode} parameter 814 * @return {@link ImeCommand} object that can be passed to 815 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 816 * wait until this event is handled by {@link MockIme} 817 */ 818 @NonNull callRequestCursorUpdates(int cursorUpdateMode)819 public ImeCommand callRequestCursorUpdates(int cursorUpdateMode) { 820 final Bundle params = new Bundle(); 821 params.putInt("cursorUpdateMode", cursorUpdateMode); 822 return callCommandInternal("requestCursorUpdates", params); 823 } 824 825 /** 826 * Lets {@link MockIme} to call {@link InputConnection#getHandler()} with the given parameters. 827 * 828 * <p>This triggers {@code getCurrentInputConnection().getHandler()}.</p> 829 * 830 * <p>Use {@link ImeEvent#isNullReturnValue()} for {@link ImeEvent} returned from 831 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 832 * value returned from the API was {@code null} or not.</p> 833 * 834 * @return {@link ImeCommand} object that can be passed to 835 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 836 * wait until this event is handled by {@link MockIme} 837 */ 838 @NonNull callGetHandler()839 public ImeCommand callGetHandler() { 840 final Bundle params = new Bundle(); 841 return callCommandInternal("getHandler", params); 842 } 843 844 /** 845 * Lets {@link MockIme} to call {@link InputConnection#closeConnection()} with the given 846 * parameters. 847 * 848 * <p>This triggers {@code getCurrentInputConnection().closeConnection()}.</p> 849 * 850 * <p>Return value information is not available for this command.</p> 851 * 852 * @return {@link ImeCommand} object that can be passed to 853 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 854 * wait until this event is handled by {@link MockIme} 855 */ 856 @NonNull callCloseConnection()857 public ImeCommand callCloseConnection() { 858 final Bundle params = new Bundle(); 859 return callCommandInternal("closeConnection", params); 860 } 861 862 /** 863 * Lets {@link MockIme} to call 864 * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} with the given 865 * parameters. 866 * 867 * <p>This triggers 868 * {@code getCurrentInputConnection().commitContent(inputContentInfo, flags, opts)}.</p> 869 * 870 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 871 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 872 * value returned from the API.</p> 873 * 874 * @param inputContentInfo to be passed as the {@code inputContentInfo} parameter 875 * @param flags to be passed as the {@code flags} parameter 876 * @param opts to be passed as the {@code opts} parameter 877 * @return {@link ImeCommand} object that can be passed to 878 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 879 * wait until this event is handled by {@link MockIme} 880 */ 881 @NonNull callCommitContent(@onNull InputContentInfo inputContentInfo, int flags, @Nullable Bundle opts)882 public ImeCommand callCommitContent(@NonNull InputContentInfo inputContentInfo, int flags, 883 @Nullable Bundle opts) { 884 final Bundle params = new Bundle(); 885 params.putParcelable("inputContentInfo", inputContentInfo); 886 params.putInt("flags", flags); 887 params.putBundle("opts", opts); 888 return callCommandInternal("commitContent", params); 889 } 890 891 /** 892 * Lets {@link MockIme} to call 893 * {@link android.inputmethodservice.InputMethodService#setBackDisposition(int)} with the given 894 * parameters. 895 * 896 * <p>This triggers {@code setBackDisposition(backDisposition)}.</p> 897 * 898 * @param backDisposition to be passed as the {@code backDisposition} parameter 899 * @return {@link ImeCommand} object that can be passed to 900 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 901 * wait until this event is handled by {@link MockIme} 902 */ 903 @NonNull callSetBackDisposition(int backDisposition)904 public ImeCommand callSetBackDisposition(int backDisposition) { 905 final Bundle params = new Bundle(); 906 params.putInt("backDisposition", backDisposition); 907 return callCommandInternal("setBackDisposition", params); 908 } 909 910 /** 911 * Lets {@link MockIme} to call 912 * {@link android.inputmethodservice.InputMethodService#requestHideSelf(int)} with the given 913 * parameters. 914 * 915 * <p>This triggers {@code requestHideSelf(flags)}.</p> 916 * 917 * @param flags to be passed as the {@code flags} parameter 918 * @return {@link ImeCommand} object that can be passed to 919 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 920 * wait until this event is handled by {@link MockIme} 921 */ 922 @NonNull callRequestHideSelf(int flags)923 public ImeCommand callRequestHideSelf(int flags) { 924 final Bundle params = new Bundle(); 925 params.putInt("flags", flags); 926 return callCommandInternal("requestHideSelf", params); 927 } 928 929 /** 930 * Lets {@link MockIme} to call 931 * {@link android.inputmethodservice.InputMethodService#requestShowSelf(int)} with the given 932 * parameters. 933 * 934 * <p>This triggers {@code requestShowSelf(flags)}.</p> 935 * 936 * @param flags to be passed as the {@code flags} parameter 937 * @return {@link ImeCommand} object that can be passed to 938 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 939 * wait until this event is handled by {@link MockIme} 940 */ 941 @NonNull callRequestShowSelf(int flags)942 public ImeCommand callRequestShowSelf(int flags) { 943 final Bundle params = new Bundle(); 944 params.putInt("flags", flags); 945 return callCommandInternal("requestShowSelf", params); 946 } 947 948 /** 949 * Lets {@link MockIme} call 950 * {@link android.inputmethodservice.InputMethodService#sendDownUpKeyEvents(int)} with the given 951 * {@code keyEventCode}. 952 * 953 * @param keyEventCode to be passed as the {@code keyEventCode} parameter. 954 * @return {@link ImeCommand} object that can be passed to 955 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 956 * wait until this event is handled by {@link MockIme} 957 */ 958 @NonNull callSendDownUpKeyEvents(int keyEventCode)959 public ImeCommand callSendDownUpKeyEvents(int keyEventCode) { 960 final Bundle params = new Bundle(); 961 params.putInt("keyEventCode", keyEventCode); 962 return callCommandInternal("sendDownUpKeyEvents", params); 963 } 964 965 @NonNull callGetDisplayId()966 public ImeCommand callGetDisplayId() { 967 final Bundle params = new Bundle(); 968 return callCommandInternal("getDisplayId", params); 969 } 970 } 971