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 android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; 20 21 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 22 23 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 24 25 import android.app.ApplicationExitInfo; 26 import android.app.UiAutomation; 27 import android.app.compat.CompatChanges; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.pm.PackageManager; 32 import android.graphics.RectF; 33 import android.os.Bundle; 34 import android.os.CancellationSignal; 35 import android.os.ParcelFileDescriptor; 36 import android.os.RemoteCallback; 37 import android.os.SystemClock; 38 import android.os.UserHandle; 39 import android.text.TextUtils; 40 import android.util.Log; 41 import android.view.KeyEvent; 42 import android.view.View; 43 import android.view.inputmethod.CompletionInfo; 44 import android.view.inputmethod.CorrectionInfo; 45 import android.view.inputmethod.DeleteGesture; 46 import android.view.inputmethod.DeleteRangeGesture; 47 import android.view.inputmethod.ExtractedTextRequest; 48 import android.view.inputmethod.HandwritingGesture; 49 import android.view.inputmethod.InputConnection; 50 import android.view.inputmethod.InputContentInfo; 51 import android.view.inputmethod.InputMethodInfo; 52 import android.view.inputmethod.InputMethodManager; 53 import android.view.inputmethod.InputMethodSubtype; 54 import android.view.inputmethod.InsertGesture; 55 import android.view.inputmethod.PreviewableHandwritingGesture; 56 import android.view.inputmethod.SelectGesture; 57 import android.view.inputmethod.SelectRangeGesture; 58 import android.view.inputmethod.TextAttribute; 59 60 import androidx.annotation.AnyThread; 61 import androidx.annotation.GuardedBy; 62 import androidx.annotation.IntRange; 63 import androidx.annotation.NonNull; 64 import androidx.annotation.Nullable; 65 import androidx.annotation.VisibleForTesting; 66 67 import com.android.compatibility.common.util.PollingCheck; 68 69 import org.junit.AssumptionViolatedException; 70 71 import java.io.IOException; 72 import java.util.List; 73 import java.util.concurrent.CountDownLatch; 74 import java.util.concurrent.Executor; 75 import java.util.concurrent.TimeUnit; 76 import java.util.concurrent.atomic.AtomicBoolean; 77 import java.util.function.Consumer; 78 import java.util.function.IntConsumer; 79 80 /** 81 * Represents an active Mock IME session, which provides basic primitives to write end-to-end tests 82 * for IME APIs. 83 * 84 * <p>To use {@link MockIme} via {@link MockImeSession}, you need to </p> 85 * <p>Public methods are not thread-safe.</p> 86 */ 87 public class MockImeSession implements AutoCloseable { 88 89 private static final String TAG = "MockImeSession"; 90 91 private final String mImeEventActionName = 92 "com.android.cts.mockime.action.IME_EVENT." + SystemClock.elapsedRealtimeNanos(); 93 94 private static final long TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10); 95 96 @NonNull 97 private final Context mContext; 98 99 @NonNull 100 private final UiAutomation mUiAutomation; 101 102 @NonNull 103 private final AtomicBoolean mActive = new AtomicBoolean(true); 104 105 @Nullable 106 private AutoCloseable mSettingsClientCloser; 107 108 @Nullable 109 private SessionChannel mChannel; 110 111 // Set with System.currentTimeMillis so it can be compatible with 112 // ApplicationExitInfo.getTimestamp() 113 private long mSessionCreateTimestamp; 114 115 @MockImePackageNames 116 @NonNull 117 private final String mMockImePackageName; 118 119 @NonNull 120 private final UserHandle mTargetUser; 121 122 @MockImePackageNames 123 @NonNull getMockImePackageName()124 public String getMockImePackageName() { 125 return mMockImePackageName; 126 } 127 128 @NonNull 129 private final String mMockImeSettingsProviderAuthority; 130 131 private final boolean mSuppressReset; 132 133 private static final class EventStore { 134 private static final int INITIAL_ARRAY_SIZE = 32; 135 136 @NonNull 137 public final ImeEvent[] mArray; 138 public int mLength; 139 EventStore()140 EventStore() { 141 mArray = new ImeEvent[INITIAL_ARRAY_SIZE]; 142 mLength = 0; 143 } 144 EventStore(EventStore src, int newLength)145 EventStore(EventStore src, int newLength) { 146 mArray = new ImeEvent[newLength]; 147 mLength = src.mLength; 148 System.arraycopy(src.mArray, 0, mArray, 0, src.mLength); 149 } 150 add(ImeEvent event)151 public EventStore add(ImeEvent event) { 152 if (mLength + 1 <= mArray.length) { 153 mArray[mLength] = event; 154 ++mLength; 155 return this; 156 } else { 157 return new EventStore(this, mLength * 2).add(event); 158 } 159 } 160 takeSnapshot()161 public ImeEventStream.ImeEventArray takeSnapshot() { 162 return new ImeEventStream.ImeEventArray(mArray, mLength); 163 } 164 } 165 166 private static final class MockImeEventReceiver implements Consumer<Bundle> { 167 private final Object mLock = new Object(); 168 169 @GuardedBy("mLock") 170 @NonNull 171 private EventStore mCurrentEventStore = new EventStore(); 172 173 @Override accept(Bundle bundle)174 public void accept(Bundle bundle) { 175 synchronized (mLock) { 176 mCurrentEventStore = 177 mCurrentEventStore.add(ImeEvent.fromBundle(bundle)); 178 } 179 } 180 takeEventSnapshot()181 public ImeEventStream.ImeEventArray takeEventSnapshot() { 182 synchronized (mLock) { 183 return mCurrentEventStore.takeSnapshot(); 184 } 185 } 186 } 187 private final MockImeEventReceiver mEventReceiver = 188 new MockImeEventReceiver(); 189 190 private final ImeEventStream mEventStream = 191 new ImeEventStream(mEventReceiver::takeEventSnapshot); 192 executeShellCommand( @onNull UiAutomation uiAutomation, @NonNull String command)193 private static String executeShellCommand( 194 @NonNull UiAutomation uiAutomation, @NonNull String command) throws IOException { 195 Log.d(TAG, "executeShellCommand(): command=" + command); 196 try (ParcelFileDescriptor.AutoCloseInputStream in = 197 new ParcelFileDescriptor.AutoCloseInputStream( 198 uiAutomation.executeShellCommand(command))) { 199 final StringBuilder sb = new StringBuilder(); 200 final byte[] buffer = new byte[4096]; 201 while (true) { 202 final int numRead = in.read(buffer); 203 if (numRead <= 0) { 204 break; 205 } 206 sb.append(new String(buffer, 0, numRead)); 207 } 208 String result = sb.toString(); 209 Log.d(TAG, "executeShellCommand(): result=" + result); 210 return result; 211 } 212 } 213 executeImeCmd(String cmd, @Nullable String...args)214 private String executeImeCmd(String cmd, @Nullable String...args) throws IOException { 215 StringBuilder fullCmd = new StringBuilder("ime ").append(cmd); 216 fullCmd.append(" --user ").append(mTargetUser.getIdentifier()).append(' '); 217 for (String arg : args) { 218 // Ideally it should check if there's more args, but adding an extra space is fine 219 fullCmd.append(' ').append(arg); 220 } 221 return executeShellCommand(mUiAutomation, fullCmd.toString()); 222 } 223 224 @Nullable getCurrentInputMethodId()225 private String getCurrentInputMethodId() { 226 final InputMethodInfo imi = 227 MultiUserUtils.getCurrentInputMethodInfoAsUser(mContext, mUiAutomation, 228 mTargetUser); 229 final String result = imi != null ? imi.getId() : null; 230 Log.v(TAG, "getCurrentInputMethodId(): returning " + result + " for user " 231 + mTargetUser.getIdentifier()); 232 return result; 233 } 234 writeMockImeSettings( @onNull String imeEventActionName, @Nullable ImeSettings.Builder imeSettings, @NonNull RemoteCallback channel)235 private void writeMockImeSettings( 236 @NonNull String imeEventActionName, 237 @Nullable ImeSettings.Builder imeSettings, 238 @NonNull RemoteCallback channel) { 239 final var bundle = ImeSettings.serializeToBundle(imeEventActionName, imeSettings, channel); 240 Log.i(TAG, "Writing MockIme settings: session=" + this + " for user=" 241 + mTargetUser.getIdentifier()); 242 MultiUserUtils.callContentProvider(mContext, mUiAutomation, 243 mMockImeSettingsProviderAuthority, "write", null /* arg */, bundle, mTargetUser); 244 } 245 setAdditionalSubtypes(@ullable InputMethodSubtype[] additionalSubtypes)246 private void setAdditionalSubtypes(@Nullable InputMethodSubtype[] additionalSubtypes) { 247 final Bundle bundle = new Bundle(); 248 bundle.putParcelableArray(SettingsProvider.SET_ADDITIONAL_SUBTYPES_KEY, additionalSubtypes); 249 MultiUserUtils.callContentProvider(mContext, mUiAutomation, 250 mMockImeSettingsProviderAuthority, SettingsProvider.SET_ADDITIONAL_SUBTYPES_COMMAND, 251 null /* arg */, bundle, mTargetUser); 252 } 253 getMockImeComponentName()254 private ComponentName getMockImeComponentName() { 255 return new ComponentName(mMockImePackageName, MockIme.class.getName()); 256 } 257 258 /** 259 * @return the IME ID of the {@link MockIme}. 260 * @see android.view.inputmethod.InputMethodInfo#getId() 261 */ getImeId()262 public String getImeId() { 263 return getMockImeComponentName().flattenToShortString(); 264 } 265 MockImeSession(@onNull Context context, @NonNull UiAutomation uiAutomation, @NonNull ImeSettings.Builder imeSettings)266 private MockImeSession(@NonNull Context context, @NonNull UiAutomation uiAutomation, 267 @NonNull ImeSettings.Builder imeSettings) { 268 mContext = context; 269 mUiAutomation = uiAutomation; 270 mMockImePackageName = imeSettings.mMockImePackageName; 271 mTargetUser = imeSettings.mTargetUser; 272 mMockImeSettingsProviderAuthority = mMockImePackageName + ".provider"; 273 mSuppressReset = imeSettings.mSuppressResetIme; 274 updateSessionCreateTimestamp(); 275 } 276 getSessionCreateTimestamp()277 public long getSessionCreateTimestamp() { 278 return mSessionCreateTimestamp; 279 } 280 updateSessionCreateTimestamp()281 private void updateSessionCreateTimestamp() { 282 mSessionCreateTimestamp = System.currentTimeMillis(); 283 } 284 285 @Nullable getInputMethodInfo()286 public InputMethodInfo getInputMethodInfo() { 287 for (InputMethodInfo imi : 288 MultiUserUtils.getInputMethodListAsUser(mContext, mUiAutomation, mTargetUser)) { 289 if (TextUtils.equals(getImeId(), imi.getId())) { 290 return imi; 291 } 292 } 293 return null; 294 } 295 initialize(@ullable ImeSettings.Builder imeSettings)296 private void initialize(@Nullable ImeSettings.Builder imeSettings) throws Exception { 297 PollingCheck.check("MockIME was not in getInputMethodList() after timeout.", TIMEOUT_MILLIS, 298 () -> getInputMethodInfo() != null); 299 300 // Make sure that MockIME is not selected. 301 if (!mSuppressReset 302 && MultiUserUtils.getInputMethodListAsUser(mContext, mUiAutomation, mTargetUser) 303 .stream() 304 .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) { 305 executeImeCmd("reset"); 306 } 307 if (MultiUserUtils.getEnabledInputMethodListAsUser(mContext, mUiAutomation, mTargetUser) 308 .stream() 309 .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) { 310 throw new IllegalStateException(); 311 } 312 313 // Make sure to set up additional subtypes before launching MockIme. 314 InputMethodSubtype[] additionalSubtypes = imeSettings.mAdditionalSubtypes; 315 if (additionalSubtypes == null) { 316 additionalSubtypes = new InputMethodSubtype[0]; 317 } 318 if (additionalSubtypes.length > 0) { 319 setAdditionalSubtypes(additionalSubtypes); 320 } else { 321 final InputMethodInfo imi = getInputMethodInfo(); 322 if (imi == null) { 323 throw new IllegalStateException("MockIME was not in getInputMethodList()."); 324 } 325 if (imi.getSubtypeCount() != 0) { 326 // Somehow the previous run failed to remove additional subtypes. Clean them up. 327 setAdditionalSubtypes(null); 328 } 329 } 330 { 331 final InputMethodInfo imi = getInputMethodInfo(); 332 if (imi == null) { 333 throw new IllegalStateException("MockIME not found while checking subtypes."); 334 } 335 if (imi.getSubtypeCount() != additionalSubtypes.length) { 336 throw new IllegalStateException("MockIME subtypes were not correctly set."); 337 } 338 } 339 340 mSettingsClientCloser = MultiUserUtils.acquireUnstableContentProviderClientSession(mContext, 341 mUiAutomation, mMockImeSettingsProviderAuthority, mTargetUser); 342 var sessionEstablished = new CountDownLatch(1); 343 mChannel = new SessionChannel(sessionEstablished::countDown); 344 mChannel.registerListener(mEventReceiver); 345 writeMockImeSettings(mImeEventActionName, imeSettings, mChannel.takeTransport()); 346 sessionEstablished.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 347 348 String imeId = getImeId(); 349 executeImeCmd("enable", imeId); 350 executeImeCmd("set", imeId); 351 352 PollingCheck.check("Make sure that MockIME becomes available", TIMEOUT_MILLIS, 353 () -> getImeId().equals(getCurrentInputMethodId())); 354 } 355 356 @Override toString()357 public String toString() { 358 return TAG + "{active=" + mActive + "}"; 359 } 360 361 /** @see #create(Context, UiAutomation, ImeSettings.Builder) */ 362 @NonNull create(@onNull Context context)363 public static MockImeSession create(@NonNull Context context) throws Exception { 364 return create(context, getInstrumentation().getUiAutomation(), new ImeSettings.Builder()); 365 } 366 367 /** 368 * Creates a new Mock IME session. During this session, you can receive various events from 369 * {@link MockIme}. 370 * 371 * @param context {@link Context} to be used to receive inter-process events from the 372 * {@link MockIme} (e.g. via {@link BroadcastReceiver} 373 * @param uiAutomation {@link UiAutomation} object to change the device state that are typically 374 * guarded by permissions. 375 * @param imeSettings Key-value pairs to be passed to the {@link MockIme}. 376 * @return A session object, with which you can retrieve event logs from the {@link MockIme} and 377 * can clean up the session. 378 */ 379 @NonNull create( @onNull Context context, @NonNull UiAutomation uiAutomation, @Nullable ImeSettings.Builder imeSettings)380 public static MockImeSession create( 381 @NonNull Context context, 382 @NonNull UiAutomation uiAutomation, 383 @Nullable ImeSettings.Builder imeSettings) throws Exception { 384 final String unavailabilityReason = getUnavailabilityReason(context); 385 if (unavailabilityReason != null) { 386 throw new AssumptionViolatedException(unavailabilityReason); 387 } 388 final MockImeSession client = new MockImeSession(context, uiAutomation, imeSettings); 389 client.initialize(imeSettings); 390 return client; 391 } 392 393 /** 394 * Checks if the {@link MockIme} can be used in this device. 395 * 396 * @return {@code null} if it can be used, or message describing why if it cannot. 397 */ 398 @Nullable getUnavailabilityReason(@onNull Context context)399 public static String getUnavailabilityReason(@NonNull Context context) { 400 if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) { 401 return "Device must support installable IMEs that implement InputMethodService API"; 402 } 403 return null; 404 } 405 406 /** 407 * Whether {@link MockIme} enabled a compatibility flag to finish input without fallback 408 * input connection when device interactive state changed. See detailed description in 409 * {@link MockImeSession#setEnabledFinishInputNoFallbackConnection}. 410 * 411 * @return {@code true} if the compatibility flag is enabled. 412 */ isFinishInputNoFallbackConnectionEnabled()413 public boolean isFinishInputNoFallbackConnectionEnabled() { 414 AtomicBoolean result = new AtomicBoolean(); 415 runWithShellPermissionIdentity(() -> 416 result.set(CompatChanges.isChangeEnabled(FINISH_INPUT_NO_FALLBACK_CONNECTION, 417 mMockImePackageName, mTargetUser))); 418 return result.get(); 419 } 420 421 /** 422 * Checks whether there are any pending IME visibility requests. 423 * 424 * @see InputMethodManager#hasPendingImeVisibilityRequests() 425 * 426 * @return {@code true} iff there are pending IME visibility requests. 427 */ hasPendingImeVisibilityRequests()428 public boolean hasPendingImeVisibilityRequests() { 429 final var imm = mContext.getSystemService(InputMethodManager.class); 430 return runWithShellPermissionIdentity(imm::hasPendingImeVisibilityRequests); 431 } 432 433 /** 434 * @return {@link ImeEventStream} object that stores events sent from {@link MockIme} since the 435 * session is created. 436 */ openEventStream()437 public ImeEventStream openEventStream() { 438 return mEventStream.copy(); 439 } 440 441 /** 442 * Logs the event stream to logcat. 443 */ logEventStream()444 public void logEventStream() { 445 Log.i(TAG, mEventStream.dump()); 446 } 447 448 /** 449 * @return {@code true} until {@link #close()} gets called. 450 */ 451 @AnyThread isActive()452 public boolean isActive() { 453 return mActive.get(); 454 } 455 456 /** 457 * Closes the active session and de-selects {@link MockIme}. Currently which IME will be 458 * selected next is up to the system. 459 */ close()460 public void close() throws Exception { 461 String exitReason = retrieveExitReasonIfMockImeCrashed(); 462 if (exitReason != null) { 463 Log.e(TAG, String.format("MockIme process exit reason: {%s}, event stream: {%s}", 464 exitReason, mEventStream.dump())); 465 } 466 467 mActive.set(false); 468 469 if (!mSuppressReset) { 470 executeImeCmd("reset"); 471 472 PollingCheck.check("Make sure that MockIME becomes unavailable", TIMEOUT_MILLIS, () -> 473 MultiUserUtils 474 .getEnabledInputMethodListAsUser(mContext, mUiAutomation, mTargetUser) 475 .stream() 476 .noneMatch( 477 info -> getMockImeComponentName().equals(info.getComponent()))); 478 } 479 480 if (mChannel != null) { 481 mChannel.close(); 482 } 483 Log.i(TAG, "Deleting MockIme settings: session=" + this); 484 MultiUserUtils.callContentProvider(mContext, mUiAutomation, 485 mMockImeSettingsProviderAuthority, "delete", null /* arg */, null /* extras */, 486 mTargetUser); 487 488 // Clean up additional subtypes if any. 489 final InputMethodInfo imi = getInputMethodInfo(); 490 if (imi != null && imi.getSubtypeCount() != 0) { 491 setAdditionalSubtypes(null); 492 } 493 if (mSettingsClientCloser != null) { 494 mSettingsClientCloser.close(); 495 mSettingsClientCloser = null; 496 } 497 updateSessionCreateTimestamp(); 498 } 499 500 @Nullable retrieveExitReasonIfMockImeCrashed()501 String retrieveExitReasonIfMockImeCrashed() { 502 final ApplicationExitInfo lastExitReason = findLatestMockImeSessionExitInfo(); 503 if (lastExitReason == null) { 504 return null; 505 } 506 if (lastExitReason.getTimestamp() <= mSessionCreateTimestamp) { 507 return null; 508 } 509 final StringBuilder err = new StringBuilder(); 510 err.append("MockIme crashed and exited with code: ").append(lastExitReason.getReason()) 511 .append("; "); 512 err.append("session create time: ").append(mSessionCreateTimestamp).append("; "); 513 err.append("process exit time: ").append(lastExitReason.getTimestamp()).append("; "); 514 err.append("see android.app.ApplicationExitInfo for more info on the exit code "); 515 final String exitDescription = lastExitReason.getDescription(); 516 if (exitDescription != null) { 517 err.append("(exit Description: ").append(exitDescription).append(")"); 518 } 519 return err.toString(); 520 } 521 522 @Nullable 523 @VisibleForTesting findLatestMockImeSessionExitInfo()524 ApplicationExitInfo findLatestMockImeSessionExitInfo() { 525 final List<ApplicationExitInfo> latestExitReasons = 526 MultiUserUtils.getHistoricalProcessExitReasons(mContext, mUiAutomation, 527 mMockImePackageName, /* pid= */ 0, /* maxNum= */ 1, mTargetUser); 528 return latestExitReasons.isEmpty() ? null : latestExitReasons.get(0); 529 } 530 531 /** 532 * Common logic to send a special command to {@link MockIme}. 533 * 534 * @param commandName command to be passed to {@link MockIme} 535 * @param params {@link Bundle} to be passed to {@link MockIme} as a parameter set of 536 * {@code commandName} 537 * @return {@link ImeCommand} that is sent to {@link MockIme}. It can be passed to 538 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 539 * wait until this event is handled by {@link MockIme}. 540 */ 541 @NonNull callCommandInternal(@onNull String commandName, @NonNull Bundle params)542 private ImeCommand callCommandInternal(@NonNull String commandName, @NonNull Bundle params) { 543 final ImeCommand command = new ImeCommand( 544 commandName, SystemClock.elapsedRealtimeNanos(), true, params); 545 if (!mChannel.send(command.toBundle())) { 546 throw new IllegalStateException("Channel already closed: " + commandName); 547 } 548 return command; 549 } 550 551 /** 552 * Lets {@link MockIme} suspend {@link MockIme.AbstractInputMethodImpl#createSession( 553 * android.view.inputmethod.InputMethod.SessionCallback)} until {@link #resumeCreateSession()}. 554 * 555 * <p>This is useful to test a tricky timing issue that the IME client initiated the 556 * IME session but {@link android.view.inputmethod.InputMethodSession} is not available 557 * yet.</p> 558 * 559 * <p>For simplicity and stability, {@link #suspendCreateSession()} must be called before 560 * {@link MockIme.AbstractInputMethodImpl#createSession( 561 * android.view.inputmethod.InputMethod.SessionCallback)} gets called again.</p> 562 * 563 * @return {@link ImeCommand} object that can be passed to 564 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 565 * wait until this event is handled by {@link MockIme}. 566 */ 567 @NonNull suspendCreateSession()568 public ImeCommand suspendCreateSession() { 569 return callCommandInternal("suspendCreateSession", new Bundle()); 570 } 571 572 /** 573 * Lets {@link MockIme} resume suspended {@link MockIme.AbstractInputMethodImpl#createSession( 574 * android.view.inputmethod.InputMethod.SessionCallback)}. 575 * 576 * <p>Does nothing if {@link #suspendCreateSession()} was not called.</p> 577 * 578 * @return {@link ImeCommand} object that can be passed to 579 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 580 * wait until this event is handled by {@link MockIme}. 581 */ 582 @NonNull resumeCreateSession()583 public ImeCommand resumeCreateSession() { 584 return callCommandInternal("resumeCreateSession", new Bundle()); 585 } 586 587 588 /** 589 * Lets {@link MockIme} to call 590 * {@link android.inputmethodservice.InputMethodService#getCurrentInputConnection()} and 591 * memorize it for later {@link InputConnection}-related operations. 592 * 593 * <p>Only the last one will be memorized if this method gets called multiple times.</p> 594 * 595 * @return {@link ImeCommand} object that can be passed to 596 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 597 * wait until this event is handled by {@link MockIme}. 598 * @see #unmemorizeCurrentInputConnection() 599 */ 600 @NonNull memorizeCurrentInputConnection()601 public ImeCommand memorizeCurrentInputConnection() { 602 final Bundle params = new Bundle(); 603 return callCommandInternal("memorizeCurrentInputConnection", params); 604 } 605 606 /** 607 * Lets {@link MockIme} to forget memorized {@link InputConnection} if any. Does nothing 608 * otherwise. 609 * 610 * @return {@link ImeCommand} object that can be passed to 611 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 612 * wait until this event is handled by {@link MockIme}. 613 * @see #memorizeCurrentInputConnection() 614 */ 615 @NonNull unmemorizeCurrentInputConnection()616 public ImeCommand unmemorizeCurrentInputConnection() { 617 final Bundle params = new Bundle(); 618 return callCommandInternal("unmemorizeCurrentInputConnection", params); 619 } 620 621 /** 622 * Lets {@link MockIme} to call {@link InputConnection#getTextBeforeCursor(int, int)} with the 623 * given parameters. 624 * 625 * <p>This triggers {@code getCurrentInputConnection().getTextBeforeCursor(n, flag)}.</p> 626 * 627 * <p>Use {@link ImeEvent#getReturnCharSequenceValue()} for {@link ImeEvent} returned from 628 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 629 * value returned from the API.</p> 630 * 631 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 632 * 633 * @param n to be passed as the {@code n} parameter. 634 * @param flag to be passed as the {@code flag} parameter. 635 * @return {@link ImeCommand} object that can be passed to 636 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 637 * wait until this event is handled by {@link MockIme}. 638 */ 639 @NonNull callGetTextBeforeCursor(int n, int flag)640 public ImeCommand callGetTextBeforeCursor(int n, int flag) { 641 final Bundle params = new Bundle(); 642 params.putInt("n", n); 643 params.putInt("flag", flag); 644 return callCommandInternal("getTextBeforeCursor", params); 645 } 646 647 /** 648 * Lets {@link MockIme} to call {@link InputConnection#getTextAfterCursor(int, int)} with the 649 * given parameters. 650 * 651 * <p>This triggers {@code getCurrentInputConnection().getTextAfterCursor(n, flag)}.</p> 652 * 653 * <p>Use {@link ImeEvent#getReturnCharSequenceValue()} for {@link ImeEvent} returned from 654 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 655 * value returned from the API.</p> 656 * 657 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 658 * 659 * @param n to be passed as the {@code n} parameter. 660 * @param flag to be passed as the {@code flag} 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 callGetTextAfterCursor(int n, int flag)666 public ImeCommand callGetTextAfterCursor(int n, int flag) { 667 final Bundle params = new Bundle(); 668 params.putInt("n", n); 669 params.putInt("flag", flag); 670 return callCommandInternal("getTextAfterCursor", params); 671 } 672 673 /** 674 * Lets {@link MockIme} to call {@link InputConnection#getSelectedText(int)} with the 675 * given parameters. 676 * 677 * <p>This triggers {@code getCurrentInputConnection().getSelectedText(flag)}.</p> 678 * 679 * <p>Use {@link ImeEvent#getReturnCharSequenceValue()} for {@link ImeEvent} returned from 680 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 681 * value returned from the API.</p> 682 * 683 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 684 * 685 * @param flag to be passed as the {@code flag} parameter. 686 * @return {@link ImeCommand} object that can be passed to 687 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 688 * wait until this event is handled by {@link MockIme}. 689 */ 690 @NonNull callGetSelectedText(int flag)691 public ImeCommand callGetSelectedText(int flag) { 692 final Bundle params = new Bundle(); 693 params.putInt("flag", flag); 694 return callCommandInternal("getSelectedText", params); 695 } 696 697 /** 698 * Lets {@link MockIme} to call {@link InputConnection#getSurroundingText(int, int, int)} with 699 * the given parameters. 700 * 701 * <p>This triggers {@code getCurrentInputConnection().getSurroundingText(int, int, int)}.</p> 702 * 703 * <p>Use {@link ImeEvent#getReturnParcelableValue()} for {@link ImeEvent} returned from 704 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 705 * value returned from the API.</p> 706 * 707 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 708 * 709 * @param beforeLength The expected length of the text before the cursor. 710 * @param afterLength The expected length of the text after the cursor. 711 * @param flags Supplies additional options controlling how the text is returned. May be either 712 * {@code 0} or {@link InputConnection#GET_TEXT_WITH_STYLES}. 713 * @return {@link ImeCommand} object that can be passed to 714 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 715 * wait until this event is handled by {@link MockIme}. 716 */ 717 @NonNull callGetSurroundingText(@ntRangefrom = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags)718 public ImeCommand callGetSurroundingText(@IntRange(from = 0) int beforeLength, 719 @IntRange(from = 0) int afterLength, int flags) { 720 final Bundle params = new Bundle(); 721 params.putInt("beforeLength", beforeLength); 722 params.putInt("afterLength", afterLength); 723 params.putInt("flags", flags); 724 return callCommandInternal("getSurroundingText", params); 725 } 726 727 /** 728 * Lets {@link MockIme} to call {@link InputConnection#getCursorCapsMode(int)} with the given 729 * parameters. 730 * 731 * <p>This triggers {@code getCurrentInputConnection().getCursorCapsMode(reqModes)}.</p> 732 * 733 * <p>Use {@link ImeEvent#getReturnIntegerValue()} for {@link ImeEvent} returned from 734 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 735 * value returned from the API.</p> 736 * 737 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 738 * 739 * @param reqModes to be passed as the {@code reqModes} parameter. 740 * @return {@link ImeCommand} object that can be passed to 741 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 742 * wait until this event is handled by {@link MockIme}. 743 */ 744 @NonNull callGetCursorCapsMode(int reqModes)745 public ImeCommand callGetCursorCapsMode(int reqModes) { 746 final Bundle params = new Bundle(); 747 params.putInt("reqModes", reqModes); 748 return callCommandInternal("getCursorCapsMode", params); 749 } 750 751 /** 752 * Lets {@link MockIme} to call 753 * {@link InputConnection#getExtractedText(ExtractedTextRequest, int)} with the given 754 * parameters. 755 * 756 * <p>This triggers {@code getCurrentInputConnection().getExtractedText(request, flags)}.</p> 757 * 758 * <p>Use {@link ImeEvent#getReturnParcelableValue()} for {@link ImeEvent} returned from 759 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 760 * value returned from the API.</p> 761 * 762 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 763 * 764 * @param request to be passed as the {@code request} parameter 765 * @param flags to be passed as the {@code flags} parameter 766 * @return {@link ImeCommand} object that can be passed to 767 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 768 * wait until this event is handled by {@link MockIme}. 769 */ 770 @NonNull callGetExtractedText(@ullable ExtractedTextRequest request, int flags)771 public ImeCommand callGetExtractedText(@Nullable ExtractedTextRequest request, int flags) { 772 final Bundle params = new Bundle(); 773 params.putParcelable("request", request); 774 params.putInt("flags", flags); 775 return callCommandInternal("getExtractedText", params); 776 } 777 778 /** 779 * Lets {@link MockIme} to call {@link InputConnection#deleteSurroundingText(int, int)} with the 780 * given parameters. 781 * 782 * <p>This triggers 783 * {@code getCurrentInputConnection().deleteSurroundingText(beforeLength, afterLength)}.</p> 784 * 785 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 786 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 787 * value returned from the API.</p> 788 * 789 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 790 * 791 * @param beforeLength to be passed as the {@code beforeLength} parameter 792 * @param afterLength to be passed as the {@code afterLength} parameter 793 * @return {@link ImeCommand} object that can be passed to 794 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 795 * wait until this event is handled by {@link MockIme}. 796 */ 797 @NonNull callDeleteSurroundingText(int beforeLength, int afterLength)798 public ImeCommand callDeleteSurroundingText(int beforeLength, int afterLength) { 799 final Bundle params = new Bundle(); 800 params.putInt("beforeLength", beforeLength); 801 params.putInt("afterLength", afterLength); 802 return callCommandInternal("deleteSurroundingText", params); 803 } 804 805 /** 806 * Lets {@link MockIme} to call 807 * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)} with the given 808 * parameters. 809 * 810 * <p>This triggers {@code getCurrentInputConnection().deleteSurroundingTextInCodePoints( 811 * beforeLength, afterLength)}.</p> 812 * 813 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 814 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 815 * value returned from the API.</p> 816 * 817 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 818 * 819 * @param beforeLength to be passed as the {@code beforeLength} parameter 820 * @param afterLength to be passed as the {@code afterLength} parameter 821 * @return {@link ImeCommand} object that can be passed to 822 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 823 * wait until this event is handled by {@link MockIme}. 824 */ 825 @NonNull callDeleteSurroundingTextInCodePoints(int beforeLength, int afterLength)826 public ImeCommand callDeleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { 827 final Bundle params = new Bundle(); 828 params.putInt("beforeLength", beforeLength); 829 params.putInt("afterLength", afterLength); 830 return callCommandInternal("deleteSurroundingTextInCodePoints", params); 831 } 832 833 /** 834 * Lets {@link MockIme} to call {@link InputConnection#setComposingText(CharSequence, int)} with 835 * the given parameters. 836 * 837 * <p>This triggers 838 * {@code getCurrentInputConnection().setComposingText(text, newCursorPosition)}.</p> 839 * 840 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 841 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 842 * value returned from the API.</p> 843 * 844 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 845 * 846 * @param text to be passed as the {@code text} parameter 847 * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter 848 * @return {@link ImeCommand} object that can be passed to 849 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 850 * wait until this event is handled by {@link MockIme}. 851 */ 852 @NonNull callSetComposingText(@ullable CharSequence text, int newCursorPosition)853 public ImeCommand callSetComposingText(@Nullable CharSequence text, int newCursorPosition) { 854 final Bundle params = new Bundle(); 855 params.putCharSequence("text", text); 856 params.putInt("newCursorPosition", newCursorPosition); 857 return callCommandInternal("setComposingText(CharSequence,int)", params); 858 } 859 860 /** 861 * Lets {@link MockIme} to call 862 * {@link InputConnection#setComposingText(CharSequence, int, TextAttribute)} with the given 863 * parameters. 864 * 865 * <p>This triggers 866 * {@code getCurrentInputConnection().setComposingText(text, newCursorPosition, textAttribute)}. 867 * </p> 868 * 869 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 870 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 871 * value returned from the API.</p> 872 * 873 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 874 * 875 * @param text to be passed as the {@code text} parameter 876 * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter 877 * @param textAttribute to be passed as the {@code textAttribute} parameter 878 * @return {@link ImeCommand} object that can be passed to 879 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 880 * wait until this event is handled by {@link MockIme} 881 */ 882 @NonNull callSetComposingText(@ullable CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute)883 public ImeCommand callSetComposingText(@Nullable CharSequence text, int newCursorPosition, 884 @Nullable TextAttribute textAttribute) { 885 final Bundle params = new Bundle(); 886 params.putCharSequence("text", text); 887 params.putInt("newCursorPosition", newCursorPosition); 888 params.putParcelable("textAttribute", textAttribute); 889 return callCommandInternal("setComposingText(CharSequence,int,TextAttribute)", params); 890 } 891 892 /** 893 * Lets {@link MockIme} to call {@link InputConnection#setComposingRegion(int, int)} with the 894 * given parameters. 895 * 896 * <p>This triggers {@code getCurrentInputConnection().setComposingRegion(start, end)}.</p> 897 * 898 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 899 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 900 * value returned from the API.</p> 901 * 902 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 903 * 904 * @param start to be passed as the {@code start} parameter 905 * @param end to be passed as the {@code end} parameter 906 * @return {@link ImeCommand} object that can be passed to 907 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 908 * wait until this event is handled by {@link MockIme}. 909 */ 910 @NonNull callSetComposingRegion(int start, int end)911 public ImeCommand callSetComposingRegion(int start, int end) { 912 final Bundle params = new Bundle(); 913 params.putInt("start", start); 914 params.putInt("end", end); 915 return callCommandInternal("setComposingRegion(int,int)", params); 916 } 917 918 /** 919 * Lets {@link MockIme} to call 920 * {@link InputConnection#setComposingRegion(int, int, TextAttribute)} with the given 921 * parameters. 922 * 923 * <p>This triggers 924 * {@code getCurrentInputConnection().setComposingRegion(start, end, TextAttribute)}.</p> 925 * 926 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 927 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 928 * value returned from the API.</p> 929 * 930 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 931 * 932 * @param start to be passed as the {@code start} parameter 933 * @param end to be passed as the {@code end} parameter 934 * @return {@link ImeCommand} object that can be passed to 935 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 936 * wait until this event is handled by {@link MockIme}. 937 */ 938 @NonNull callSetComposingRegion(int start, int end, @Nullable TextAttribute textAttribute)939 public ImeCommand callSetComposingRegion(int start, int end, 940 @Nullable TextAttribute textAttribute) { 941 final Bundle params = new Bundle(); 942 params.putInt("start", start); 943 params.putInt("end", end); 944 params.putParcelable("textAttribute", textAttribute); 945 return callCommandInternal("setComposingRegion(int,int,TextAttribute)", params); 946 } 947 948 /** 949 * Lets {@link MockIme} to call {@link InputConnection#finishComposingText()} with the given 950 * parameters. 951 * 952 * <p>This triggers {@code getCurrentInputConnection().finishComposingText()}.</p> 953 * 954 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 955 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 956 * value returned from the API.</p> 957 * 958 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 959 * 960 * @return {@link ImeCommand} object that can be passed to 961 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 962 * wait until this event is handled by {@link MockIme}. 963 */ 964 @NonNull callFinishComposingText()965 public ImeCommand callFinishComposingText() { 966 final Bundle params = new Bundle(); 967 return callCommandInternal("finishComposingText", params); 968 } 969 970 /** 971 * Lets {@link MockIme} to call {@link InputConnection#commitText(CharSequence, int)} with the 972 * given parameters. 973 * 974 * <p>This triggers {@code getCurrentInputConnection().commitText(text, newCursorPosition)}.</p> 975 * 976 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 977 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 978 * value returned from the API.</p> 979 * 980 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 981 * 982 * @param text to be passed as the {@code text} parameter 983 * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter 984 * @return {@link ImeCommand} object that can be passed to 985 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 986 * wait until this event is handled by {@link MockIme} 987 */ 988 @NonNull callCommitText(@ullable CharSequence text, int newCursorPosition)989 public ImeCommand callCommitText(@Nullable CharSequence text, int newCursorPosition) { 990 final Bundle params = new Bundle(); 991 params.putCharSequence("text", text); 992 params.putInt("newCursorPosition", newCursorPosition); 993 return callCommandInternal("commitText(CharSequence,int)", params); 994 } 995 996 /** 997 * Lets {@link MockIme} to call 998 * {@link InputConnection#commitText(CharSequence, int, TextAttribute)} with the given 999 * parameters. 1000 * 1001 * <p>This triggers 1002 * {@code getCurrentInputConnection().commitText(text, newCursorPosition, TextAttribute)}.</p> 1003 * 1004 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1005 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1006 * value returned from the API.</p> 1007 * 1008 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1009 * 1010 * @param text to be passed as the {@code text} parameter 1011 * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter 1012 * @return {@link ImeCommand} object that can be passed to 1013 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1014 * wait until this event is handled by {@link MockIme} 1015 */ 1016 @NonNull callCommitText(@ullable CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute)1017 public ImeCommand callCommitText(@Nullable CharSequence text, int newCursorPosition, 1018 @Nullable TextAttribute textAttribute) { 1019 final Bundle params = new Bundle(); 1020 params.putCharSequence("text", text); 1021 params.putInt("newCursorPosition", newCursorPosition); 1022 params.putParcelable("textAttribute", textAttribute); 1023 return callCommandInternal("commitText(CharSequence,int,TextAttribute)", params); 1024 } 1025 1026 /** 1027 * Lets {@link MockIme} to call {@link InputConnection#commitCompletion(CompletionInfo)} with 1028 * the given parameters. 1029 * 1030 * <p>This triggers {@code getCurrentInputConnection().commitCompletion(text)}.</p> 1031 * 1032 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1033 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1034 * value returned from the API.</p> 1035 * 1036 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1037 * 1038 * @param text to be passed as the {@code text} parameter 1039 * @return {@link ImeCommand} object that can be passed to 1040 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1041 * wait until this event is handled by {@link MockIme} 1042 */ 1043 @NonNull callCommitCompletion(@ullable CompletionInfo text)1044 public ImeCommand callCommitCompletion(@Nullable CompletionInfo text) { 1045 final Bundle params = new Bundle(); 1046 params.putParcelable("text", text); 1047 return callCommandInternal("commitCompletion", params); 1048 } 1049 1050 /** 1051 * Lets {@link MockIme} to call {@link InputConnection#commitCorrection(CorrectionInfo)} with 1052 * the given parameters. 1053 * 1054 * <p>This triggers {@code getCurrentInputConnection().commitCorrection(correctionInfo)}.</p> 1055 * 1056 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1057 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1058 * value returned from the API.</p> 1059 * 1060 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1061 * 1062 * @param correctionInfo to be passed as the {@code correctionInfo} parameter 1063 * @return {@link ImeCommand} object that can be passed to 1064 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1065 * wait until this event is handled by {@link MockIme} 1066 */ 1067 @NonNull callCommitCorrection(@ullable CorrectionInfo correctionInfo)1068 public ImeCommand callCommitCorrection(@Nullable CorrectionInfo correctionInfo) { 1069 final Bundle params = new Bundle(); 1070 params.putParcelable("correctionInfo", correctionInfo); 1071 return callCommandInternal("commitCorrection", params); 1072 } 1073 1074 /** 1075 * Lets {@link MockIme} to call {@link InputConnection#setSelection(int, int)} with the given 1076 * parameters. 1077 * 1078 * <p>This triggers {@code getCurrentInputConnection().setSelection(start, end)}.</p> 1079 * 1080 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1081 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1082 * value returned from the API.</p> 1083 * 1084 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1085 * 1086 * @param start to be passed as the {@code start} parameter 1087 * @param end to be passed as the {@code end} parameter 1088 * @return {@link ImeCommand} object that can be passed to 1089 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1090 * wait until this event is handled by {@link MockIme} 1091 */ 1092 @NonNull callSetSelection(int start, int end)1093 public ImeCommand callSetSelection(int start, int end) { 1094 final Bundle params = new Bundle(); 1095 params.putInt("start", start); 1096 params.putInt("end", end); 1097 return callCommandInternal("setSelection", params); 1098 } 1099 1100 /** 1101 * Lets {@link MockIme} to call {@link InputConnection#performEditorAction(int)} with the given 1102 * parameters. 1103 * 1104 * <p>This triggers {@code getCurrentInputConnection().performEditorAction(editorAction)}.</p> 1105 * 1106 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1107 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1108 * value returned from the API.</p> 1109 * 1110 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1111 * 1112 * @param editorAction to be passed as the {@code editorAction} parameter 1113 * @return {@link ImeCommand} object that can be passed to 1114 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1115 * wait until this event is handled by {@link MockIme} 1116 */ 1117 @NonNull callPerformEditorAction(int editorAction)1118 public ImeCommand callPerformEditorAction(int editorAction) { 1119 final Bundle params = new Bundle(); 1120 params.putInt("editorAction", editorAction); 1121 return callCommandInternal("performEditorAction", params); 1122 } 1123 1124 /** 1125 * Lets {@link MockIme} to call {@link InputConnection#performContextMenuAction(int)} with the 1126 * given parameters. 1127 * 1128 * <p>This triggers {@code getCurrentInputConnection().performContextMenuAction(id)}.</p> 1129 * 1130 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1131 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1132 * value returned from the API.</p> 1133 * 1134 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1135 * 1136 * @param id to be passed as the {@code id} parameter 1137 * @return {@link ImeCommand} object that can be passed to 1138 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1139 * wait until this event is handled by {@link MockIme} 1140 */ 1141 @NonNull callPerformContextMenuAction(int id)1142 public ImeCommand callPerformContextMenuAction(int id) { 1143 final Bundle params = new Bundle(); 1144 params.putInt("id", id); 1145 return callCommandInternal("performContextMenuAction", params); 1146 } 1147 1148 /** 1149 * Lets {@link MockIme} to call {@link InputConnection#beginBatchEdit()} with the given 1150 * parameters. 1151 * 1152 * <p>This triggers {@code getCurrentInputConnection().beginBatchEdit()}.</p> 1153 * 1154 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1155 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1156 * value returned from the API.</p> 1157 * 1158 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1159 * 1160 * @return {@link ImeCommand} object that can be passed to 1161 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1162 * wait until this event is handled by {@link MockIme} 1163 */ 1164 @NonNull callBeginBatchEdit()1165 public ImeCommand callBeginBatchEdit() { 1166 final Bundle params = new Bundle(); 1167 return callCommandInternal("beginBatchEdit", params); 1168 } 1169 1170 /** 1171 * Lets {@link MockIme} to call {@link InputConnection#endBatchEdit()} with the given 1172 * parameters. 1173 * 1174 * <p>This triggers {@code getCurrentInputConnection().endBatchEdit()}.</p> 1175 * 1176 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1177 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1178 * value returned from the API.</p> 1179 * 1180 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1181 * 1182 * @return {@link ImeCommand} object that can be passed to 1183 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1184 * wait until this event is handled by {@link MockIme} 1185 */ 1186 @NonNull callEndBatchEdit()1187 public ImeCommand callEndBatchEdit() { 1188 final Bundle params = new Bundle(); 1189 return callCommandInternal("endBatchEdit", params); 1190 } 1191 1192 /** 1193 * Lets {@link MockIme} to call {@link InputConnection#sendKeyEvent(KeyEvent)} with the given 1194 * parameters. 1195 * 1196 * <p>This triggers {@code getCurrentInputConnection().sendKeyEvent(event)}.</p> 1197 * 1198 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1199 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1200 * value returned from the API.</p> 1201 * 1202 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1203 * 1204 * @param event to be passed as the {@code event} parameter 1205 * @return {@link ImeCommand} object that can be passed to 1206 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1207 * wait until this event is handled by {@link MockIme} 1208 */ 1209 @NonNull callSendKeyEvent(@ullable KeyEvent event)1210 public ImeCommand callSendKeyEvent(@Nullable KeyEvent event) { 1211 final Bundle params = new Bundle(); 1212 params.putParcelable("event", event); 1213 return callCommandInternal("sendKeyEvent", params); 1214 } 1215 1216 /** 1217 * Lets {@link MockIme} to call {@link InputConnection#performSpellCheck()}. 1218 * 1219 * <p>This triggers {@code getCurrentInputConnection().performSpellCheck()}.</p> 1220 * 1221 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1222 1223 * @return {@link ImeCommand} object that can be passed to 1224 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1225 * wait until this event is handled by {@link MockIme} 1226 */ 1227 @NonNull callPerformSpellCheck()1228 public ImeCommand callPerformSpellCheck() { 1229 return callCommandInternal("performSpellCheck", new Bundle()); 1230 } 1231 1232 /** 1233 * Lets {@link MockIme} to call {@link InputConnection#takeSnapshot()}. 1234 * 1235 * <p>This triggers {@code getCurrentInputConnection().takeSnapshot()}.</p> 1236 * 1237 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1238 1239 * @return {@link ImeCommand} object that can be passed to 1240 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1241 * wait until this event is handled by {@link MockIme} 1242 */ 1243 @NonNull callTakeSnapshot()1244 public ImeCommand callTakeSnapshot() { 1245 return callCommandInternal("takeSnapshot", new Bundle()); 1246 } 1247 1248 /** 1249 * Lets {@link MockIme} to call {@link InputConnection#clearMetaKeyStates(int)} with the given 1250 * parameters. 1251 * 1252 * <p>This triggers {@code getCurrentInputConnection().sendKeyEvent(event)}.</p> 1253 * 1254 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1255 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1256 * value returned from the API.</p> 1257 * 1258 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1259 * 1260 * @param states to be passed as the {@code states} parameter 1261 * @return {@link ImeCommand} object that can be passed to 1262 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1263 * wait until this event is handled by {@link MockIme} 1264 */ 1265 @NonNull callClearMetaKeyStates(int states)1266 public ImeCommand callClearMetaKeyStates(int states) { 1267 final Bundle params = new Bundle(); 1268 params.putInt("states", states); 1269 return callCommandInternal("clearMetaKeyStates", params); 1270 } 1271 1272 /** 1273 * Lets {@link MockIme} to call {@link InputConnection#reportFullscreenMode(boolean)} with the 1274 * given parameters. 1275 * 1276 * <p>This triggers {@code getCurrentInputConnection().reportFullscreenMode(enabled)}.</p> 1277 * 1278 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1279 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1280 * value returned from the API.</p> 1281 * 1282 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1283 * 1284 * @param enabled to be passed as the {@code enabled} parameter 1285 * @return {@link ImeCommand} object that can be passed to 1286 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1287 * wait until this event is handled by {@link MockIme} 1288 */ 1289 @NonNull callReportFullscreenMode(boolean enabled)1290 public ImeCommand callReportFullscreenMode(boolean enabled) { 1291 final Bundle params = new Bundle(); 1292 params.putBoolean("enabled", enabled); 1293 return callCommandInternal("reportFullscreenMode", params); 1294 } 1295 1296 /** 1297 * Lets {@link MockIme} to call {@link InputConnection#performPrivateCommand(String, Bundle)} 1298 * with the given parameters. 1299 * 1300 * <p>This triggers {@code getCurrentInputConnection().performPrivateCommand(action, data)}.</p> 1301 * 1302 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1303 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1304 * value returned from the API.</p> 1305 * 1306 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1307 * 1308 * @param action to be passed as the {@code action} parameter 1309 * @param data to be passed as the {@code data} parameter 1310 * @return {@link ImeCommand} object that can be passed to 1311 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1312 * wait until this event is handled by {@link MockIme} 1313 */ 1314 @NonNull callPerformPrivateCommand(@ullable String action, Bundle data)1315 public ImeCommand callPerformPrivateCommand(@Nullable String action, Bundle data) { 1316 final Bundle params = new Bundle(); 1317 params.putString("action", action); 1318 params.putBundle("data", data); 1319 return callCommandInternal("performPrivateCommand", params); 1320 } 1321 1322 /** 1323 * Lets {@link MockIme} to call 1324 * {@link InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)} 1325 * with the given parameters. 1326 * 1327 * <p>The result callback will be recorded as an {@code onPerformHandwritingGestureResult} 1328 * event. 1329 * 1330 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1331 * 1332 * @param gesture {@link SelectGesture} or {@link InsertGesture} or {@link DeleteGesture}. 1333 * @param useDelayedCancellation {@code true} to use delayed {@link CancellationSignal#cancel()} 1334 * on a supported gesture like {@link android.view.inputmethod.InsertModeGesture}. 1335 * @return {@link ImeCommand} object that can be passed to 1336 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1337 * wait until this event is handled by {@link MockIme}. 1338 */ 1339 @NonNull callPerformHandwritingGesture( @onNull HandwritingGesture gesture, boolean useDelayedCancellation)1340 public ImeCommand callPerformHandwritingGesture( 1341 @NonNull HandwritingGesture gesture, boolean useDelayedCancellation) { 1342 final Bundle params = new Bundle(); 1343 params.putByteArray("gesture", gesture.toByteArray()); 1344 params.putBoolean("useDelayedCancellation", useDelayedCancellation); 1345 return callCommandInternal("performHandwritingGesture", params); 1346 } 1347 1348 /** 1349 * Lets {@link MockIme} to call {@link InputConnection#requestTextBoundsInfo}. 1350 * 1351 * <p>The result callback will be recorded as an {@code onRequestTextBoundsInfoResult} event. 1352 * 1353 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1354 * 1355 * @param gesture {@link SelectGesture} or {@link InsertGesture} or {@link DeleteGesture}. 1356 * @return {@link ImeCommand} object that can be passed to 1357 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1358 * wait until this event is handled by {@link MockIme}. 1359 */ 1360 @NonNull callRequestTextBoundsInfo(RectF rectF)1361 public ImeCommand callRequestTextBoundsInfo(RectF rectF) { 1362 final Bundle params = new Bundle(); 1363 params.putParcelable("rectF", rectF); 1364 return callCommandInternal("requestTextBoundsInfo", params); 1365 } 1366 1367 /** 1368 * Lets {@link MockIme} to call 1369 * {@link InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, 1370 * CancellationSignal)} with the given parameters. 1371 * 1372 * <p>Use {@link ImeEvent#getReturnIntegerValue()} for {@link ImeEvent} returned from 1373 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1374 * value returned from the API.</p> 1375 * 1376 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1377 * 1378 * @param gesture one of {@link SelectGesture}, {@link SelectRangeGesture}, 1379 * {@link DeleteGesture}, {@link DeleteRangeGesture}. 1380 * @param useDelayedCancellation {@code true} to use delayed {@link CancellationSignal#cancel()} 1381 * on a gesture preview. 1382 * @return {@link ImeCommand} object that can be passed to 1383 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1384 * wait until this event is handled by {@link MockIme}. 1385 */ 1386 @NonNull callPreviewHandwritingGesture( @onNull PreviewableHandwritingGesture gesture, boolean useDelayedCancellation)1387 public ImeCommand callPreviewHandwritingGesture( 1388 @NonNull PreviewableHandwritingGesture gesture, boolean useDelayedCancellation) { 1389 final Bundle params = new Bundle(); 1390 params.putByteArray("gesture", gesture.toByteArray()); 1391 params.putBoolean("useDelayedCancellation", useDelayedCancellation); 1392 return callCommandInternal("previewHandwritingGesture", params); 1393 } 1394 1395 /** 1396 * Lets {@link MockIme} to call {@link InputConnection#requestCursorUpdates(int)} with the given 1397 * parameters. 1398 * 1399 * <p>This triggers {@code getCurrentInputConnection().requestCursorUpdates(cursorUpdateMode)}. 1400 * </p> 1401 * 1402 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1403 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1404 * value returned from the API.</p> 1405 * 1406 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1407 * 1408 * @param cursorUpdateMode to be passed as the {@code cursorUpdateMode} parameter 1409 * @return {@link ImeCommand} object that can be passed to 1410 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1411 * wait until this event is handled by {@link MockIme} 1412 */ 1413 @NonNull callRequestCursorUpdates(int cursorUpdateMode)1414 public ImeCommand callRequestCursorUpdates(int cursorUpdateMode) { 1415 final Bundle params = new Bundle(); 1416 params.putInt("cursorUpdateMode", cursorUpdateMode); 1417 return callCommandInternal("requestCursorUpdates", params); 1418 } 1419 1420 /** 1421 * Lets {@link MockIme} to call {@link InputConnection#requestCursorUpdates(int, int)} with the 1422 * given parameters. 1423 * 1424 * <p>This triggers {@code getCurrentInputConnection().requestCursorUpdates( 1425 * cursorUpdateMode, cursorUpdateFilter)}. 1426 * </p> 1427 * 1428 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1429 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1430 * value returned from the API.</p> 1431 * 1432 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1433 * 1434 * @param cursorUpdateMode to be passed as the {@code cursorUpdateMode} parameter 1435 * @param cursorUpdateFilter to be passed as the {@code cursorUpdateFilter} parameter 1436 * @return {@link ImeCommand} object that can be passed to 1437 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1438 * wait until this event is handled by {@link MockIme} 1439 */ 1440 @NonNull callRequestCursorUpdates(int cursorUpdateMode, int cursorUpdateFilter)1441 public ImeCommand callRequestCursorUpdates(int cursorUpdateMode, int cursorUpdateFilter) { 1442 final Bundle params = new Bundle(); 1443 params.putInt("cursorUpdateMode", cursorUpdateMode); 1444 params.putInt("cursorUpdateFilter", cursorUpdateFilter); 1445 return callCommandInternal("requestCursorUpdates", params); 1446 } 1447 1448 /** 1449 * Lets {@link MockIme} to call {@link InputConnection#getHandler()} with the given parameters. 1450 * 1451 * <p>This triggers {@code getCurrentInputConnection().getHandler()}.</p> 1452 * 1453 * <p>Use {@link ImeEvent#isNullReturnValue()} for {@link ImeEvent} returned from 1454 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1455 * value returned from the API was {@code null} or not.</p> 1456 * 1457 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1458 * 1459 * @return {@link ImeCommand} object that can be passed to 1460 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1461 * wait until this event is handled by {@link MockIme} 1462 */ 1463 @NonNull callGetHandler()1464 public ImeCommand callGetHandler() { 1465 final Bundle params = new Bundle(); 1466 return callCommandInternal("getHandler", params); 1467 } 1468 1469 /** 1470 * Lets {@link MockIme} to call {@link InputConnection#closeConnection()} with the given 1471 * parameters. 1472 * 1473 * <p>This triggers {@code getCurrentInputConnection().closeConnection()}.</p> 1474 * 1475 * <p>Return value information is not available for this command.</p> 1476 * 1477 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1478 * 1479 * @return {@link ImeCommand} object that can be passed to 1480 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1481 * wait until this event is handled by {@link MockIme} 1482 */ 1483 @NonNull callCloseConnection()1484 public ImeCommand callCloseConnection() { 1485 final Bundle params = new Bundle(); 1486 return callCommandInternal("closeConnection", params); 1487 } 1488 1489 /** 1490 * Lets {@link MockIme} to call 1491 * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} with the given 1492 * parameters. 1493 * 1494 * <p>This triggers 1495 * {@code getCurrentInputConnection().commitContent(inputContentInfo, flags, opts)}.</p> 1496 * 1497 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1498 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1499 * value returned from the API.</p> 1500 * 1501 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1502 * 1503 * @param inputContentInfo to be passed as the {@code inputContentInfo} parameter 1504 * @param flags to be passed as the {@code flags} parameter 1505 * @param opts to be passed as the {@code opts} parameter 1506 * @return {@link ImeCommand} object that can be passed to 1507 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1508 * wait until this event is handled by {@link MockIme} 1509 */ 1510 @NonNull callCommitContent(@onNull InputContentInfo inputContentInfo, int flags, @Nullable Bundle opts)1511 public ImeCommand callCommitContent(@NonNull InputContentInfo inputContentInfo, int flags, 1512 @Nullable Bundle opts) { 1513 final Bundle params = new Bundle(); 1514 params.putParcelable("inputContentInfo", inputContentInfo); 1515 params.putInt("flags", flags); 1516 params.putBundle("opts", opts); 1517 return callCommandInternal("commitContent", params); 1518 } 1519 1520 /** 1521 * Lets {@link MockIme} to call {@link InputConnection#setImeConsumesInput(boolean)} with the 1522 * given parameters. 1523 * 1524 * <p>This triggers {@code getCurrentInputConnection().setImeConsumesInput(boolean)}.</p> 1525 * 1526 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from 1527 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the 1528 * value returned from the API.</p> 1529 * 1530 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}.</p> 1531 * 1532 * @param imeConsumesInput to be passed as the {@code imeConsumesInput} parameter 1533 * @return {@link ImeCommand} object that can be passed to 1534 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1535 * wait until this event is handled by {@link MockIme} 1536 */ 1537 @NonNull callSetImeConsumesInput(boolean imeConsumesInput)1538 public ImeCommand callSetImeConsumesInput(boolean imeConsumesInput) { 1539 final Bundle params = new Bundle(); 1540 params.putBoolean("imeConsumesInput", imeConsumesInput); 1541 return callCommandInternal("setImeConsumesInput", params); 1542 } 1543 1544 /** 1545 * Lets {@link MockIme} to call {@link InputConnection#replaceText(int, int, CharSequence, int, 1546 * TextAttribute)} with the given parameters. 1547 * 1548 * <p>This triggers {@code getCurrentInputConnection().replaceText(int, int, CharSequence, int, 1549 * TextAttribute)}. 1550 * 1551 * <p>Use {@link ImeEvent#getReturnBooleanValue()} for {@link ImeEvent} returned from {@link 1552 * ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to see the value 1553 * returned from the API. 1554 * 1555 * <p>This can be affected by {@link #memorizeCurrentInputConnection()}. 1556 * 1557 * @param start the character index where the replacement should start 1558 * @param end the character index where the replacement should end 1559 * @param newCursorPosition the new cursor position around the text. If > 0, this is relative to 1560 * the end of the text - 1; if <= 0, this is relative to the start of the text. So a value 1561 * of 1 will always advance you to the position after the full text being inserted. Note 1562 * that this means you can't position the cursor within the text. 1563 * @param text the text to replace. This may include styles. 1564 * @param textAttribute The extra information about the text. This value may be null. 1565 * @return {@link ImeCommand} object that can be passed to {@link 1566 * ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to wait until 1567 * this event is handled by {@link MockIme} 1568 */ 1569 @NonNull callReplaceText( int start, int end, @NonNull CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute)1570 public ImeCommand callReplaceText( 1571 int start, 1572 int end, 1573 @NonNull CharSequence text, 1574 int newCursorPosition, 1575 @Nullable TextAttribute textAttribute) { 1576 final Bundle params = new Bundle(); 1577 params.putInt("start", start); 1578 params.putInt("end", end); 1579 params.putCharSequence("text", text); 1580 params.putInt("newCursorPosition", newCursorPosition); 1581 params.putParcelable("textAttribute", textAttribute); 1582 return callCommandInternal("replaceText", params); 1583 } 1584 1585 /** 1586 * Lets {@link MockIme} to call 1587 * {@link InputMethodManager#setExplicitlyEnabledInputMethodSubtypes(String, int[])} with the 1588 * given parameters. 1589 * 1590 * <p>This triggers {@code setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes)}. 1591 * </p> 1592 * 1593 * @param imeId the IME ID. 1594 * @param subtypeHashCodes An array of {@link InputMethodSubtype#hashCode()}. An empty array and 1595 * {@code null} can reset the enabled subtypes. 1596 * @return {@link ImeCommand} object that can be passed to 1597 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1598 * wait until this event is handled by {@link MockIme} 1599 */ 1600 @NonNull callSetExplicitlyEnabledInputMethodSubtypes(String imeId, @Nullable int[] subtypeHashCodes)1601 public ImeCommand callSetExplicitlyEnabledInputMethodSubtypes(String imeId, 1602 @Nullable int[] subtypeHashCodes) { 1603 final Bundle params = new Bundle(); 1604 params.putString("imeId", imeId); 1605 params.putIntArray("subtypeHashCodes", subtypeHashCodes); 1606 return callCommandInternal("setExplicitlyEnabledInputMethodSubtypes", params); 1607 } 1608 1609 /** 1610 * Lets {@link MockIme} to call 1611 * {@link InputMethodManager#setAdditionalInputMethodSubtypes(String, InputMethodSubtype[])} 1612 * with the given parameters. 1613 * 1614 * @param imeId the IME ID 1615 * @param subtypes A non-null array of {@link InputMethodSubtype} 1616 * @return {@link ImeCommand} object that can be passed to 1617 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1618 * wait until this event is handled by {@link MockIme} 1619 */ 1620 @NonNull callSetAdditionalInputMethodSubtypes(@onNull String imeId, @NonNull InputMethodSubtype[] subtypes)1621 public ImeCommand callSetAdditionalInputMethodSubtypes(@NonNull String imeId, 1622 @NonNull InputMethodSubtype[] subtypes) { 1623 final Bundle params = new Bundle(); 1624 params.putString("imeId", imeId); 1625 params.putParcelableArray("subtypes", subtypes); 1626 return callCommandInternal("setAdditionalInputMethodSubtypes", params); 1627 } 1628 1629 /** 1630 * Makes {@link MockIme} call {@link 1631 * android.inputmethodservice.InputMethodService#switchInputMethod(String)} 1632 * with the given parameters. 1633 * 1634 * @param id the IME ID. 1635 * @return {@link ImeCommand} object that can be passed to 1636 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1637 * wait until this event is handled by {@link MockIme} 1638 */ 1639 @NonNull callSwitchInputMethod(String id)1640 public ImeCommand callSwitchInputMethod(String id) { 1641 final Bundle params = new Bundle(); 1642 params.putString("id", id); 1643 return callCommandInternal("switchInputMethod", params); 1644 } 1645 1646 /** 1647 * Lets {@link MockIme} to call {@link 1648 * android.inputmethodservice.InputMethodService#switchInputMethod(String, InputMethodSubtype)} 1649 * with the given parameters. 1650 * 1651 * <p>This triggers {@code switchInputMethod(id, subtype)}.</p> 1652 * 1653 * @param id the IME ID. 1654 * @param subtype {@link InputMethodSubtype} to be switched to. Ignored if {@code null}. 1655 * @return {@link ImeCommand} object that can be passed to 1656 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1657 * wait until this event is handled by {@link MockIme} 1658 */ 1659 @NonNull callSwitchInputMethod(String id, @Nullable InputMethodSubtype subtype)1660 public ImeCommand callSwitchInputMethod(String id, @Nullable InputMethodSubtype subtype) { 1661 final Bundle params = new Bundle(); 1662 params.putString("id", id); 1663 params.putParcelable("subtype", subtype); 1664 return callCommandInternal("switchInputMethod(String,InputMethodSubtype)", params); 1665 } 1666 1667 /** 1668 * Lets {@link MockIme} to call 1669 * {@link android.inputmethodservice.InputMethodService#setBackDisposition(int)} with the given 1670 * parameters. 1671 * 1672 * <p>This triggers {@code setBackDisposition(backDisposition)}.</p> 1673 * 1674 * @param backDisposition to be passed as the {@code backDisposition} parameter 1675 * @return {@link ImeCommand} object that can be passed to 1676 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1677 * wait until this event is handled by {@link MockIme} 1678 */ 1679 @NonNull callSetBackDisposition(int backDisposition)1680 public ImeCommand callSetBackDisposition(int backDisposition) { 1681 final Bundle params = new Bundle(); 1682 params.putInt("backDisposition", backDisposition); 1683 return callCommandInternal("setBackDisposition", params); 1684 } 1685 1686 /** 1687 * Lets {@link MockIme} to call 1688 * {@link android.inputmethodservice.InputMethodService#requestHideSelf(int)} with the given 1689 * parameters. 1690 * 1691 * <p>This triggers {@code requestHideSelf(flags)}.</p> 1692 * 1693 * @param flags to be passed as the {@code flags} parameter 1694 * @return {@link ImeCommand} object that can be passed to 1695 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1696 * wait until this event is handled by {@link MockIme} 1697 */ 1698 @NonNull callRequestHideSelf(int flags)1699 public ImeCommand callRequestHideSelf(int flags) { 1700 final Bundle params = new Bundle(); 1701 params.putInt("flags", flags); 1702 return callCommandInternal("requestHideSelf", params); 1703 } 1704 1705 /** 1706 * Lets {@link MockIme} to call 1707 * {@link android.inputmethodservice.InputMethodService#requestShowSelf(int)} with the given 1708 * parameters. 1709 * 1710 * <p>This triggers {@code requestShowSelf(flags)}.</p> 1711 * 1712 * @param flags to be passed as the {@code flags} parameter 1713 * @return {@link ImeCommand} object that can be passed to 1714 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1715 * wait until this event is handled by {@link MockIme} 1716 */ 1717 @NonNull callRequestShowSelf(int flags)1718 public ImeCommand callRequestShowSelf(int flags) { 1719 final Bundle params = new Bundle(); 1720 params.putInt("flags", flags); 1721 return callCommandInternal("requestShowSelf", params); 1722 } 1723 1724 /** 1725 * Requests hiding the current soft input window, with the request origin on the server side. 1726 * 1727 * @see InputMethodManager#hideSoftInputFromServerForTest() 1728 */ hideSoftInputFromServerForTest()1729 public void hideSoftInputFromServerForTest() { 1730 final var imm = mContext.getSystemService(InputMethodManager.class); 1731 runWithShellPermissionIdentity(imm::hideSoftInputFromServerForTest); 1732 } 1733 1734 /** 1735 * Lets {@link MockIme} call 1736 * {@link android.inputmethodservice.InputMethodService#sendDownUpKeyEvents(int)} with the given 1737 * {@code keyEventCode}. 1738 * 1739 * @param keyEventCode to be passed as the {@code keyEventCode} parameter. 1740 * @return {@link ImeCommand} object that can be passed to 1741 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1742 * wait until this event is handled by {@link MockIme} 1743 */ 1744 @NonNull callSendDownUpKeyEvents(int keyEventCode)1745 public ImeCommand callSendDownUpKeyEvents(int keyEventCode) { 1746 final Bundle params = new Bundle(); 1747 params.putInt("keyEventCode", keyEventCode); 1748 return callCommandInternal("sendDownUpKeyEvents", params); 1749 } 1750 1751 /** 1752 * Lets {@link MockIme} call 1753 * {@link android.content.pm.PackageManager#getApplicationInfo(String, int)} with the given 1754 * {@code packageName} and {@code flags}. 1755 * 1756 * @param packageName the package name to be passed to 1757 * {@link android.content.pm.PackageManager#getApplicationInfo(String, int)}. 1758 * @param flags the flags to be passed to 1759 * {@link android.content.pm.PackageManager#getApplicationInfo(String, int)}. 1760 * @return {@link ImeCommand} object that can be passed to 1761 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1762 * wait until this event is handled by {@link MockIme}. 1763 */ 1764 @NonNull callGetApplicationInfo(@onNull String packageName, int flags)1765 public ImeCommand callGetApplicationInfo(@NonNull String packageName, int flags) { 1766 final Bundle params = new Bundle(); 1767 params.putString("packageName", packageName); 1768 params.putInt("flags", flags); 1769 return callCommandInternal("getApplicationInfo", params); 1770 } 1771 1772 @NonNull callSetEnableOnBackInvokedCallback(Boolean isEnabled)1773 public ImeCommand callSetEnableOnBackInvokedCallback(Boolean isEnabled) { 1774 final Bundle params = new Bundle(); 1775 params.putBoolean("isEnabled", isEnabled); 1776 return callCommandInternal("setEnableOnBackInvokedCallback", params); 1777 } 1778 1779 @NonNull callGetDisplayId()1780 public ImeCommand callGetDisplayId() { 1781 final Bundle params = new Bundle(); 1782 return callCommandInternal("getDisplayId", params); 1783 } 1784 1785 /** 1786 * Calls and returns value of 1787 * {@link android.inputmethodservice.InputMethodService#onEvaluateFullscreenMode}. 1788 */ 1789 @NonNull callGetOnEvaluateFullscreenMode()1790 public ImeCommand callGetOnEvaluateFullscreenMode() { 1791 return callCommandInternal("getOnEvaluateFullscreenMode", new Bundle()); 1792 } 1793 1794 /** 1795 * Verifies {@code InputMethodService.getLayoutInflater().getContext()} is equal to 1796 * {@code InputMethodService.this}. 1797 * 1798 * @return {@link ImeCommand} object that can be passed to 1799 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1800 * wait until this event is handled by {@link MockIme} 1801 */ 1802 @NonNull verifyLayoutInflaterContext()1803 public ImeCommand verifyLayoutInflaterContext() { 1804 final Bundle params = new Bundle(); 1805 return callCommandInternal("verifyLayoutInflaterContext", params); 1806 } 1807 1808 @NonNull callSetHeight(int height)1809 public ImeCommand callSetHeight(int height) { 1810 final Bundle params = new Bundle(); 1811 params.putInt("height", height); 1812 return callCommandInternal("setHeight", params); 1813 } 1814 1815 @NonNull callSetInlineSuggestionsExtras(@onNull Bundle bundle)1816 public void callSetInlineSuggestionsExtras(@NonNull Bundle bundle) { 1817 MultiUserUtils.callContentProvider(mContext, mUiAutomation, 1818 mMockImeSettingsProviderAuthority, 1819 SettingsProvider.SET_INLINE_SUGGESTION_EXTRAS_COMMAND, null /* args */, bundle, 1820 mTargetUser); 1821 } 1822 1823 /** 1824 * Lets {@link MockIme} call 1825 * {@link android.inputmethodservice.InputMethodService#setExtractView(View)} with a custom 1826 * extract view hierarchy. 1827 * 1828 * @param label The label text to show in the extract view hierarchy. 1829 * @return {@link ImeCommand} object that can be passed to 1830 * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to 1831 * wait until this event is handled by {@link MockIme}. 1832 */ 1833 @NonNull callSetExtractView(String label)1834 public ImeCommand callSetExtractView(String label) { 1835 Bundle params = new Bundle(); 1836 params.putString("label", label); 1837 return callCommandInternal("setExtractView", params); 1838 } 1839 1840 @NonNull callVerifyExtractViewNotNull()1841 public ImeCommand callVerifyExtractViewNotNull() { 1842 return callCommandInternal("verifyExtractViewNotNull", new Bundle()); 1843 } 1844 1845 @NonNull callVerifyGetDisplay()1846 public ImeCommand callVerifyGetDisplay() { 1847 return callCommandInternal("verifyGetDisplay", new Bundle()); 1848 } 1849 1850 @NonNull callVerifyIsUiContext()1851 public ImeCommand callVerifyIsUiContext() { 1852 return callCommandInternal("verifyIsUiContext", new Bundle()); 1853 } 1854 1855 @NonNull callVerifyGetWindowManager()1856 public ImeCommand callVerifyGetWindowManager() { 1857 return callCommandInternal("verifyGetWindowManager", new Bundle()); 1858 } 1859 1860 @NonNull callVerifyGetViewConfiguration()1861 public ImeCommand callVerifyGetViewConfiguration() { 1862 return callCommandInternal("verifyGetViewConfiguration", new Bundle()); 1863 } 1864 1865 @NonNull callVerifyGetGestureDetector()1866 public ImeCommand callVerifyGetGestureDetector() { 1867 return callCommandInternal("verifyGetGestureDetector", new Bundle()); 1868 } 1869 1870 @NonNull callVerifyGetWindowManagerOnDisplayContext()1871 public ImeCommand callVerifyGetWindowManagerOnDisplayContext() { 1872 return callCommandInternal("verifyGetWindowManagerOnDisplayContext", new Bundle()); 1873 } 1874 1875 @NonNull callVerifyGetViewConfigurationOnDisplayContext()1876 public ImeCommand callVerifyGetViewConfigurationOnDisplayContext() { 1877 return callCommandInternal("verifyGetViewConfigurationOnDisplayContext", new Bundle()); 1878 } 1879 1880 @NonNull callVerifyGetGestureDetectorOnDisplayContext()1881 public ImeCommand callVerifyGetGestureDetectorOnDisplayContext() { 1882 return callCommandInternal("verifyGetGestureDetectorOnDisplayContext", new Bundle()); 1883 } 1884 1885 @NonNull callGetStylusHandwritingWindowVisibility()1886 public ImeCommand callGetStylusHandwritingWindowVisibility() { 1887 return callCommandInternal("getStylusHandwritingWindowVisibility", new Bundle()); 1888 } 1889 1890 @NonNull callGetWindowLayoutInfo()1891 public ImeCommand callGetWindowLayoutInfo() { 1892 return callCommandInternal("getWindowLayoutInfo", new Bundle()); 1893 } 1894 1895 @NonNull callHasStylusHandwritingWindow()1896 public ImeCommand callHasStylusHandwritingWindow() { 1897 return callCommandInternal("hasStylusHandwritingWindow", new Bundle()); 1898 } 1899 1900 @NonNull callSetStylusHandwritingInkView()1901 public ImeCommand callSetStylusHandwritingInkView() { 1902 return callCommandInternal("setStylusHandwritingInkView", new Bundle()); 1903 } 1904 1905 @NonNull callSetStylusHandwritingTimeout(long timeoutMs)1906 public ImeCommand callSetStylusHandwritingTimeout(long timeoutMs) { 1907 Bundle params = new Bundle(); 1908 params.putLong("timeoutMs", timeoutMs); 1909 return callCommandInternal("setStylusHandwritingTimeout", params); 1910 } 1911 1912 @NonNull callGetStylusHandwritingTimeout()1913 public ImeCommand callGetStylusHandwritingTimeout() { 1914 return callCommandInternal("getStylusHandwritingTimeout", new Bundle()); 1915 } 1916 1917 @NonNull callGetStylusHandwritingEvents()1918 public ImeCommand callGetStylusHandwritingEvents() { 1919 return callCommandInternal("getStylusHandwritingEvents", new Bundle()); 1920 } 1921 1922 @NonNull callFinishStylusHandwriting()1923 public ImeCommand callFinishStylusHandwriting() { 1924 return callCommandInternal("finishStylusHandwriting", new Bundle()); 1925 } 1926 1927 /** 1928 * Lets {@link MockIme} call 1929 * {@link android.inputmethodservice.InputMethodService#finishConnectionlessStylusHandwriting} 1930 * with the given {@code text}. 1931 */ 1932 @NonNull callFinishConnectionlessStylusHandwriting(CharSequence text)1933 public ImeCommand callFinishConnectionlessStylusHandwriting(CharSequence text) { 1934 Bundle params = new Bundle(); 1935 params.putCharSequence("text", text); 1936 return callCommandInternal("finishConnectionlessStylusHandwriting", params); 1937 } 1938 1939 @NonNull callGetCurrentWindowMetricsBounds()1940 public ImeCommand callGetCurrentWindowMetricsBounds() { 1941 return callCommandInternal("getCurrentWindowMetricsBounds", new Bundle()); 1942 } 1943 1944 @NonNull callSetImeCaptionBarVisible(boolean visible)1945 public ImeCommand callSetImeCaptionBarVisible(boolean visible) { 1946 final Bundle params = new Bundle(); 1947 params.putBoolean("visible", visible); 1948 return callCommandInternal("setImeCaptionBarVisible", params); 1949 } 1950 1951 @NonNull callGetImeCaptionBarHeight()1952 public ImeCommand callGetImeCaptionBarHeight() { 1953 return callCommandInternal("getImeCaptionBarHeight", new Bundle()); 1954 } 1955 } 1956