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