xref: /aosp_15_r20/cts/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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