Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a80d7212 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari Committed by Android (Google) Code Review
Browse files

Merge "[2/2] Add API to insert text on CC display using InputConnection" into main

parents 08fc3ca8 259a1693
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -55,7 +55,8 @@ interface IComputerControlSession {
            int width, int height, in Surface surface);

    /**
     * Inserts text into the current active input connection (if available).
     * Inserts text into the current active input connection. If there is no active input
     * connection, this method is no-op.
     *
     * @param text to be inserted
     * @param replaceExisting whether the existing text in the input field should be replaced. If
+5 −3
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteComputerControlInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.InputMethodInfoSafeList;
import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -298,6 +299,7 @@ final class IInputMethodManagerGlobalInvoker {
            @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
            @Nullable IRemoteInputConnection remoteInputConnection,
            @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
            @Nullable IRemoteComputerControlInputConnection remoteComputerControlInputConnection,
            int unverifiedTargetSdkVersion, @UserIdInt int userId,
            @NonNull ResultReceiver imeBackCallbackReceiver, boolean imeRequestedVisible) {
        final IInputMethodManager service = getService();
@@ -307,9 +309,9 @@ final class IInputMethodManagerGlobalInvoker {
        try {
            service.startInputOrWindowGainedFocus(startInputReason, client, windowToken,
                    startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
                    imeBackCallbackReceiver, imeRequestedVisible,
                    advanceAngGetStartInputSequenceNumber());
                    remoteAccessibilityInputConnection, remoteComputerControlInputConnection,
                    unverifiedTargetSdkVersion, userId, imeBackCallbackReceiver,
                    imeRequestedVisible, advanceAngGetStartInputSequenceNumber());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+10 −4
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@ import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IInputMethodSession;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteComputerControlInputConnection;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -940,8 +941,7 @@ public final class InputMethodManager {
                        StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
                        viewForWindowFocus.getWindowToken(), startInputFlags, softInputMode,
                        windowFlags,
                        null,
                        null, null,
                        null, null, null, null,
                        mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
                        UserHandle.myUserId(), mImeBackCallbackProxy.getResultReceiver(),
                        imeRequestedVisible);
@@ -3635,13 +3635,19 @@ public final class InputMethodManager {
                    ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus");

            final IRemoteAccessibilityInputConnection accessibilityInputConnection =
                    servedInputConnection == null ? null
                            : servedInputConnection.asIRemoteAccessibilityInputConnection();
            final IRemoteComputerControlInputConnection computerControlInputConnection =
                    (!android.companion.virtualdevice.flags.Flags.computerControlTyping()
                            || servedInputConnection == null) ? null
                            : servedInputConnection.asIRemoteComputerControlInputConnection();
            // async result delivered via MSG_START_INPUT_RESULT.
            final int startInputSeq =
                    IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
                            startInputReason, mClient, windowGainingFocus, startInputFlags,
                            softInputMode, windowFlags, editorInfo, servedInputConnection,
                            servedInputConnection == null ? null
                                    : servedInputConnection.asIRemoteAccessibilityInputConnection(),
                            accessibilityInputConnection, computerControlInputConnection,
                            view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
                            mImeBackCallbackProxy.getResultReceiver(), imeRequestedVisible);

+65 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.view.ViewRootImpl;

import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteComputerControlInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputConnectionCommandHeader;
@@ -1436,6 +1437,70 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
        return mAccessibilityInputConnection;
    }

    private final IRemoteComputerControlInputConnection mComputerControlInputConnection =
            new IRemoteComputerControlInputConnection.Stub() {

                @Dispatching(cancellable = true)
                @Override
                public void commitText(@NonNull InputConnectionCommandHeader header,
                        @NonNull CharSequence text, int newCursorPosition) {
                    dispatchWithTracing("commitTextFromComputerControl", () -> {
                        if (!checkSessionId(header)) {
                            return; // cancelled.
                        }
                        InputConnection ic = getInputConnection();
                        if (ic == null || mDeactivateRequested.get()) {
                            Log.w(TAG, "commitText on inactive InputConnection");
                            return;
                        }
                        ic.commitText(text, newCursorPosition);
                    });
                }

                @Dispatching(cancellable = true)
                @Override
                public void replaceText(@NonNull InputConnectionCommandHeader header, int start,
                        int end, @NonNull CharSequence text, int newCursorPosition) {
                    dispatchWithTracing("replaceTextFromComputerControl", () -> {
                        if (!checkSessionId(header)) {
                            return; // cancelled.
                        }
                        InputConnection ic = getInputConnection();
                        if (ic == null || mDeactivateRequested.get()) {
                            Log.w(TAG, "replaceText on inactive InputConnection");
                            return;
                        }
                        ic.replaceText(start, end, text, newCursorPosition,
                                null /* textAttribute */);
                    });
                }

                @Dispatching(cancellable = true)
                @Override
                public void sendKeyEvent(@NonNull InputConnectionCommandHeader header,
                        @NonNull KeyEvent event) {
                    dispatchWithTracing("sendKeyEventFromComputerControl", () -> {
                        if (!checkSessionId(header)) {
                            return; // cancelled.
                        }
                        InputConnection ic = getInputConnection();
                        if (ic == null || mDeactivateRequested.get()) {
                            Log.w(TAG, "sendKeyEvent on inactive InputConnection");
                            return;
                        }
                        ic.sendKeyEvent(event);
                    });
                }
            };

    /**
     * @return {@link IRemoteComputerControlInputConnection} associated with this object.
     */
    @NonNull
    public IRemoteComputerControlInputConnection asIRemoteComputerControlInputConnection() {
        return mComputerControlInputConnection;
    }

    private void dispatch(@NonNull Runnable runnable) {
        // If we are calling this from the target thread, then we can call right through.
        // Otherwise, we need to send the message to the target thread.
+80 −0
Original line number Diff line number Diff line
/*
 * Copyright 2025 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.view.KeyEvent;
import android.view.inputmethod.TextAttribute;
import com.android.internal.inputmethod.InputConnectionCommandHeader;

/**
 * Interface from computer control session to the application, allowing it to perform edits on the
 * current input field and other interactions with the application. These methods call into
 * corresponding {@link InputConnection} methods.
 */
oneway interface IRemoteComputerControlInputConnection {

    /**
     * Commit text to the text box and set the new cursor position.
     *
     * <p>This method commits the contents of the currently composing text, and then moves the
     * cursor according to {@code newCursorPosition}. If there is no composing text when this
     * method is called, the new text is inserted at the cursor position, removing text inside the
     * selection if any.
     *
     * @param text The text to commit. This may include styles.
     * @param newCursorPosition The new cursor position around the text. If > 0, this is relative to
     *                          the end of the text - 1; if <= 0, this is relative to the start
     *                          of the text. So a value of 1 will always advance the cursor to the
     *                          position after the full text being inserted.
     *
     * @see InputConnection#commitText(CharSequence, int)
     */
    void commitText(in InputConnectionCommandHeader header, in CharSequence text,
                    int newCursorPosition);

    /**
     * Replace the specific range in the editor with suggested text.
     *
     * <p>This method finishes whatever composing text is currently active and leaves the text
     * as-it, replaces the specific range of text with the passed CharSequence, and then moves the
     * cursor according to {@code newCursorPosition}.
     *
     * @param start the character index where the replacement should start.
     * @param end the character index where the replacement should end.
     * @param newCursorPosition The new cursor position around the text. If > 0, this is relative to
     *                          the end of the text - 1; if <= 0, this is relative to the start
     *                          of the text. So a value of 1 will always advance the cursor to the
     *                          position after the full text being inserted.
     * @param text the text to replace. This may include styles.
     *
     * @see InputConnection#replaceText(int, int, CharSequence, int, TextAttribute)
     */
    void replaceText(in InputConnectionCommandHeader header, int start, int end,
                     in CharSequence text, int newCursorPosition);

    /**
     * Send a key event to the process that is currently attached through this input connection.
     * The event will be dispatched like a normal key event, to the currently focused view; this
     * generally is the view that is providing this {@link InputConnection}.
     *
     * @param event The key event.
     *
     * @see KeyEvent
     * @see InputConnection#sendKeyEvent(KeyEvent)
     */
    void sendKeyEvent(in InputConnectionCommandHeader header, in KeyEvent event);
}
Loading