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