/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.inputmethod; import android.annotation.AnyThread; import android.annotation.DurationMillisLong; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.view.KeyEvent; import android.view.inputmethod.SurroundingText; import android.view.inputmethod.TextAttribute; import com.android.internal.infra.AndroidFuture; import java.util.concurrent.CompletableFuture; /** * A wrapper object for A11y IME. * *

This needs to be public to be referenced from {@link android.app.UiAutomation}.

*/ public final class RemoteAccessibilityInputConnection { private static final String TAG = "RemoteA11yInputConnection"; @DurationMillisLong private static final int MAX_WAIT_TIME_MILLIS = 2000; @NonNull IRemoteAccessibilityInputConnectionInvoker mInvoker; /** * Signaled when the system decided to take away IME focus from the target app. * *

This is expected to be signaled immediately when the IME process receives * {@link com.android.internal.inputmethod.IInputMethod#unbindInput()}.

*/ @NonNull private final CancellationGroup mCancellationGroup; public RemoteAccessibilityInputConnection( @NonNull IRemoteAccessibilityInputConnection connection, @NonNull CancellationGroup cancellationGroup) { mInvoker = IRemoteAccessibilityInputConnectionInvoker.create(connection); mCancellationGroup = cancellationGroup; } public RemoteAccessibilityInputConnection(@NonNull RemoteAccessibilityInputConnection original, int sessionId) { mInvoker = original.mInvoker.cloneWithSessionId(sessionId); mCancellationGroup = original.mCancellationGroup; } /** * Test if this object holds the given {@link IRemoteAccessibilityInputConnection} or not. * * @param connection {@link IRemoteAccessibilityInputConnection} to be tested. * @return {@code true} if this object holds the same object. */ @AnyThread public boolean isSameConnection(@NonNull IRemoteAccessibilityInputConnection connection) { return mInvoker.isSameConnection(connection); } /** * Invokes {@link IRemoteAccessibilityInputConnection#commitText(InputConnectionCommandHeader, * CharSequence, int, TextAttribute)}. * * @param text The {@code "text"} parameter to be passed. * @param newCursorPosition The {@code "newCursorPosition"} parameter to be passed. * @param textAttribute The {@code "textAttribute"} parameter to be passed. */ @AnyThread public void commitText(@NonNull CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) { mInvoker.commitText(text, newCursorPosition, textAttribute); } /** * Invokes {@link IRemoteAccessibilityInputConnection#setSelection(InputConnectionCommandHeader, * int, int)}. * * @param start The {@code "start"} parameter to be passed. * @param end The {@code "end"} parameter to be passed. */ @AnyThread public void setSelection(int start, int end) { mInvoker.setSelection(start, end); } /** * Invokes {@link IRemoteAccessibilityInputConnection#getSurroundingText( * InputConnectionCommandHeader, int, int, int, AndroidFuture)}. * * @param beforeLength The {@code "beforeLength"} parameter to be passed. * @param afterLength The {@code "afterLength"} parameter to be passed. * @param flags The {@code "flags"} parameter to be passed. * @return The {@link SurroundingText} object returned from the target application. */ @AnyThread public SurroundingText getSurroundingText( @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) { if (mCancellationGroup.isCanceled()) { return null; } final CompletableFuture value = mInvoker.getSurroundingText(beforeLength, afterLength, flags); return CompletableFutureUtil.getResultOrNull( value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } /** * Invokes {@link IRemoteAccessibilityInputConnection#deleteSurroundingText( * InputConnectionCommandHeader, int, int)}. * * @param beforeLength The {@code "beforeLength"} parameter to be passed. * @param afterLength The {@code "afterLength"} parameter to be passed. */ @AnyThread public void deleteSurroundingText(int beforeLength, int afterLength) { mInvoker.deleteSurroundingText(beforeLength, afterLength); } /** * Invokes {@link IRemoteAccessibilityInputConnection#sendKeyEvent(InputConnectionCommandHeader, * KeyEvent)}. * * @param event The {@code "event"} parameter to be passed. */ @AnyThread public void sendKeyEvent(KeyEvent event) { mInvoker.sendKeyEvent(event); } /** * Invokes {@link IRemoteAccessibilityInputConnection#performEditorAction( * InputConnectionCommandHeader, int)}. * * @param actionCode The {@code "actionCode"} parameter to be passed. */ @AnyThread public void performEditorAction(int actionCode) { mInvoker.performEditorAction(actionCode); } /** * Invokes {@link IRemoteAccessibilityInputConnection#performContextMenuAction( * InputConnectionCommandHeader, int)}. * * @param id The {@code "id"} parameter to be passed. */ @AnyThread public void performContextMenuAction(int id) { mInvoker.performContextMenuAction(id); } /** * Invokes {@link IRemoteAccessibilityInputConnection#getCursorCapsMode( * InputConnectionCommandHeader, int, AndroidFuture)}. * * @param reqModes The {@code "reqModes"} parameter to be passed. * @return integer result returned from the target application. */ @AnyThread public int getCursorCapsMode(int reqModes) { if (mCancellationGroup.isCanceled()) { return 0; } final CompletableFuture value = mInvoker.getCursorCapsMode(reqModes); return CompletableFutureUtil.getResultOrZero( value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } /** * Invokes {@link IRemoteAccessibilityInputConnection#clearMetaKeyStates( * InputConnectionCommandHeader, int)}. * * @param states The {@code "states"} parameter to be passed. */ @AnyThread public void clearMetaKeyStates(int states) { mInvoker.clearMetaKeyStates(states); } }