| /* |
| * 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. |
| * |
| * <p>This needs to be public to be referenced from {@link android.app.UiAutomation}.</p> |
| */ |
| 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. |
| * |
| * <p>This is expected to be signaled immediately when the IME process receives |
| * {@link com.android.internal.inputmethod.IInputMethod#unbindInput()}.</p> |
| */ |
| @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<SurroundingText> 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<Integer> 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); |
| } |
| } |