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