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

Commit 6fd8d5af authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add InputConnection#takeSnapshot()"

parents aad5d619 dae47962
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -52201,6 +52201,7 @@ package android.view.inputmethod {
    method public boolean setComposingText(CharSequence, int);
    method public default boolean setImeConsumesInput(boolean);
    method public boolean setSelection(int, int);
    method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
    field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
    field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
    field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1
@@ -52413,6 +52414,16 @@ package android.view.inputmethod {
    field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
  }
  public final class TextSnapshot {
    ctor public TextSnapshot(@NonNull android.view.inputmethod.SurroundingText, @IntRange(from=0xffffffff) int, @IntRange(from=0xffffffff) int, int);
    method @IntRange(from=0xffffffff) public int getCompositionEnd();
    method @IntRange(from=0xffffffff) public int getCompositionStart();
    method public int getCursorCapsMode();
    method @IntRange(from=0xffffffff) public int getSelectionEnd();
    method @IntRange(from=0xffffffff) public int getSelectionStart();
    method @NonNull public android.view.inputmethod.SurroundingText getSurroundingText();
  }
}
package android.view.inspector {
+34 −0
Original line number Diff line number Diff line
@@ -979,4 +979,38 @@ public class BaseInputConnection implements InputConnection {
                .build();
        return mTargetView.performReceiveContent(payload) == null;
    }

    /**
     * Default implementation that constructs {@link TextSnapshot} with information extracted from
     * {@link BaseInputConnection}.
     *
     * @return {@code null} when {@link TextSnapshot} cannot be fully taken.
     */
    @Nullable
    @Override
    public TextSnapshot takeSnapshot() {
        final Editable content = getEditable();
        if (content == null) {
            return null;
        }
        int composingStart = getComposingSpanStart(content);
        int composingEnd = getComposingSpanEnd(content);
        if (composingEnd < composingStart) {
            final int tmp = composingStart;
            composingStart = composingEnd;
            composingEnd = tmp;
        }

        final SurroundingText surroundingText = getSurroundingText(
                EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH / 2,
                EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH / 2, GET_TEXT_WITH_STYLES);
        if (surroundingText == null) {
            return null;
        }

        final int cursorCapsMode = getCursorCapsMode(TextUtils.CAP_MODE_CHARACTERS
                | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES);

        return new TextSnapshot(surroundingText, composingStart, composingEnd, cursorCapsMode);
    }
}
+23 −0
Original line number Diff line number Diff line
@@ -1034,4 +1034,27 @@ public interface InputConnection {
    default boolean setImeConsumesInput(boolean imeConsumesInput) {
        return false;
    }

    /**
     * Called by the system when it needs to take a snapshot of multiple text-related data in an
     * atomic manner.
     *
     * <p><strong>Editor authors</strong>: Supporting this method is strongly encouraged. Atomically
     * taken {@link TextSnapshot} is going to be really helpful for the system when optimizing IPCs
     * in a safe and deterministic manner.  Return {@code null} if an atomically taken
     * {@link TextSnapshot} is unavailable.  The system continues supporting such a scenario
     * gracefully.</p>
     *
     * <p><strong>IME authors</strong>: Currently IMEs cannot call this method directly and always
     * receive {@code null} as the result.</p>
     *
     * @return {@code null} if {@link TextSnapshot} is unavailable and/or this API is called from
     *         IMEs.
     */
    @Nullable
    default TextSnapshot takeSnapshot() {
        // Returning null by default because the composing text range cannot be retrieved from
        // existing APIs.
        return null;
    }
}
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.view.inputmethod;

import android.annotation.IntRange;
import android.annotation.NonNull;

import java.util.Objects;

/**
 * An immutable data snapshot of text editing state.
 */
public final class TextSnapshot {
    @NonNull
    private final SurroundingText mSurroundingText;
    @IntRange(from = -1)
    private final int mCompositionStart;
    @IntRange(from = -1)
    private final int mCompositionEnd;
    private final int mCursorCapsMode;

    /**
     * Creates a new instance of {@link TextSnapshot}
     *
     * @param surroundingText {@link SurroundingText} of the current edit field.
     * @param compositionStart The start index of the composing text.
     *                         {@code -1} if there is no composing text.
     * @param compositionEnd The end index of the composing text.
     *                       {@code -1} if there is no composing text.
     * @param cursorCapsMode The capitalization mode of the first character being edited in the
     *                       text.  See {@link EditorInfo#initialCapsMode}.
     * @throws NullPointerException if {@code surroundingText} is {@code null}.
     * @throws IllegalArgumentException if {@code compositionStart} and/or {@code compositionEnd}
     *                                  is less than {@code -1}.
     */
    public TextSnapshot(@NonNull SurroundingText surroundingText,
            @IntRange(from = -1) int compositionStart, @IntRange(from = -1) int compositionEnd,
            int cursorCapsMode) {
        Objects.requireNonNull(surroundingText);
        mSurroundingText = surroundingText;
        if (compositionStart < -1) {
            throw new IllegalArgumentException("compositionStart must be -1 or higher but was "
                    + compositionStart);
        }
        if (compositionEnd < -1) {
            throw new IllegalArgumentException("compositionEnd must be -1 or higher but was "
                    + compositionEnd);
        }
        if (compositionStart == -1 && compositionEnd != -1) {
            throw new IllegalArgumentException("compositionEnd must be -1 if compositionStart is "
                    + "-1 but was " + compositionEnd);
        }
        if (compositionStart != -1 && compositionEnd == -1) {
            throw new IllegalArgumentException("compositionStart must be -1 if compositionEnd is "
                    + "-1 but was " + compositionStart);
        }
        if (compositionStart > compositionEnd) {
            throw new IllegalArgumentException("compositionStart=" + compositionStart + " must be"
                    + " equal to or greater than compositionEnd=" + compositionEnd);
        }
        mCompositionStart = compositionStart;
        mCompositionEnd = compositionEnd;
        mCursorCapsMode = cursorCapsMode;
    }

    /**
     * @return {@link SurroundingText} of the current edit field.
     */
    @NonNull
    public SurroundingText getSurroundingText() {
        return mSurroundingText;
    }

    /**
     * @return The start index of the selection range. {@code -1} if it is not available.
     */
    @IntRange(from = -1)
    public int getSelectionStart() {
        if (mSurroundingText.getOffset() < 0) {
            return -1;
        }
        return mSurroundingText.getSelectionStart() + mSurroundingText.getOffset();
    }

    /**
     * @return The end index of the selection range. {@code -1} if it is not available.
     */
    @IntRange(from = -1)
    public int getSelectionEnd() {
        if (mSurroundingText.getOffset() < 0) {
            return -1;
        }
        return mSurroundingText.getSelectionEnd() + mSurroundingText.getOffset();
    }

    /**
     * @return The end index of the composing text. {@code -1} if there is no composing text.
     */
    @IntRange(from = -1)
    public int getCompositionStart() {
        return mCompositionStart;
    }

    /**
     * @return The end index of the composing text. {@code -1} if there is no composing text.
     */
    @IntRange(from = -1)
    public int getCompositionEnd() {
        return mCompositionEnd;
    }

    /**
     * The capitalization mode of the first character being edited in the text.
     *
     * <p>Values may be any combination of the following values:</p>
     * <ul>
     *     <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_CHARACTERS}</li>
     *     <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_WORDS}</li>
     *     <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_SENTENCES}</li>
     * </ul>
     *
     * <p>You should generally just take a non-zero value to mean "start out in caps mode" though.
     * </p>
     * @see EditorInfo#initialCapsMode
     */
    public int getCursorCapsMode() {
        return mCursorCapsMode;
    }
}