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

Commit 62af809a authored by Hiroki Sato's avatar Hiroki Sato
Browse files

Reland CursorAnchorInfo#createForAdditionalParentMatrix()

This logically relands the CL [1], which added an @hide method
CursorAnchorInfo#createForAdditionalParentMatrix().
The corresponding change was once removed in [2].
This is a preparation to reland a change that supports
InputMethodManager#reportActivityView() again.

[1] Ic7f9057623ffc61ec7a6121735dc39adecf4649d
[2] I0a05453eeed863db5bfe4b965ab4bcbad67f2783

Bug: 224424149
Test: CursorAnchorInfoTest
Change-Id: I0a411453c7c26795d28e913a96be8eb8fa3684d7
parent ba57fccd
Loading
Loading
Loading
Loading
+83 −21
Original line number Diff line number Diff line
@@ -415,7 +415,7 @@ public final class CursorAnchorInfo implements Parcelable {
                            "required when positional parameters are specified.");
                }
            }
            return new CursorAnchorInfo(this);
            return CursorAnchorInfo.create(this);
        }

        /**
@@ -440,31 +440,93 @@ public final class CursorAnchorInfo implements Parcelable {
        }
    }

    private CursorAnchorInfo(final Builder builder) {
        mSelectionStart = builder.mSelectionStart;
        mSelectionEnd = builder.mSelectionEnd;
        mComposingTextStart = builder.mComposingTextStart;
        mComposingText = builder.mComposingText;
        mInsertionMarkerFlags = builder.mInsertionMarkerFlags;
        mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal;
        mInsertionMarkerTop = builder.mInsertionMarkerTop;
        mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
        mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
        mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null
                ? builder.mCharacterBoundsArrayBuilder.build() : null;
        mEditorBoundsInfo = builder.mEditorBoundsInfo;
        mMatrixValues = new float[9];
    private static CursorAnchorInfo create(Builder builder) {
        final SparseRectFArray characterBoundsArray =
                builder.mCharacterBoundsArrayBuilder != null
                        ? builder.mCharacterBoundsArrayBuilder.build()
                        : null;
        final float[] matrixValues = new float[9];
        if (builder.mMatrixInitialized) {
            System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
            System.arraycopy(builder.mMatrixValues, 0, matrixValues, 0, 9);
        } else {
            Matrix.IDENTITY_MATRIX.getValues(mMatrixValues);
            Matrix.IDENTITY_MATRIX.getValues(matrixValues);
        }

        return new CursorAnchorInfo(builder.mSelectionStart, builder.mSelectionEnd,
                builder.mComposingTextStart, builder.mComposingText, builder.mInsertionMarkerFlags,
                builder.mInsertionMarkerHorizontal, builder.mInsertionMarkerTop,
                builder.mInsertionMarkerBaseline, builder.mInsertionMarkerBottom,
                characterBoundsArray, builder.mEditorBoundsInfo, matrixValues);
    }

    private CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart,
            @Nullable CharSequence composingText, int insertionMarkerFlags,
            float insertionMarkerHorizontal, float insertionMarkerTop,
            float insertionMarkerBaseline, float insertionMarkerBottom,
            @Nullable SparseRectFArray characterBoundsArray,
            @Nullable EditorBoundsInfo editorBoundsInfo,
            @NonNull float[] matrixValues) {
        mSelectionStart = selectionStart;
        mSelectionEnd = selectionEnd;
        mComposingTextStart = composingTextStart;
        mComposingText = composingText;
        mInsertionMarkerFlags = insertionMarkerFlags;
        mInsertionMarkerHorizontal = insertionMarkerHorizontal;
        mInsertionMarkerTop = insertionMarkerTop;
        mInsertionMarkerBaseline = insertionMarkerBaseline;
        mInsertionMarkerBottom = insertionMarkerBottom;
        mCharacterBoundsArray = characterBoundsArray;
        mEditorBoundsInfo = editorBoundsInfo;
        mMatrixValues = matrixValues;

        // To keep hash function simple, we only use some complex objects for hash.
        int hash = Objects.hashCode(mComposingText);
        hash *= 31;
        hash += Arrays.hashCode(mMatrixValues);
        mHashCode = hash;
        int hashCode = Objects.hashCode(mComposingText);
        hashCode *= 31;
        hashCode += Arrays.hashCode(mMatrixValues);
        mHashCode = hashCode;
    }

    /**
     * Creates a new instance of {@link CursorAnchorInfo} by applying {@code parentMatrix} to
     * the coordinate transformation matrix.
     *
     * @param original     {@link CursorAnchorInfo} to be cloned from.
     * @param parentMatrix {@link Matrix} to be applied to {@code original.getMatrix()}
     * @return A new instance of {@link CursorAnchorInfo} whose {@link CursorAnchorInfo#getMatrix()}
     *         returns {@code parentMatrix * original.getMatrix()}.
     * @hide
     */
    public static CursorAnchorInfo createForAdditionalParentMatrix(CursorAnchorInfo original,
            @NonNull Matrix parentMatrix) {
        return new CursorAnchorInfo(original.mSelectionStart, original.mSelectionEnd,
                original.mComposingTextStart, original.mComposingText,
                original.mInsertionMarkerFlags, original.mInsertionMarkerHorizontal,
                original.mInsertionMarkerTop, original.mInsertionMarkerBaseline,
                original.mInsertionMarkerBottom, original.mCharacterBoundsArray,
                original.mEditorBoundsInfo, computeMatrixValues(parentMatrix, original));
    }

    /**
     * Returns a float array that represents {@link Matrix} elements for
     * {@code parentMatrix * info.getMatrix()}.
     *
     * @param parentMatrix {@link Matrix} to be multiplied.
     * @param info         {@link CursorAnchorInfo} to provide {@link Matrix} to be multiplied.
     * @return {@code parentMatrix * info.getMatrix()}.
     */
    private static float[] computeMatrixValues(@NonNull Matrix parentMatrix,
            @NonNull CursorAnchorInfo info) {
        if (parentMatrix.isIdentity()) {
            return info.mMatrixValues;
        }

        final Matrix newMatrix = new Matrix();
        newMatrix.setValues(info.mMatrixValues);
        newMatrix.postConcat(parentMatrix);

        final float[] matrixValues = new float[9];
        newMatrix.getValues(matrixValues);
        return matrixValues;
    }

    /**
+63 −0
Original line number Diff line number Diff line
/*
 * 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 android.view.inputmethod;

import static org.junit.Assert.assertEquals;

import android.graphics.Matrix;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class CursorAnchorInfoTest {
    @Test
    public void testCreateForAdditionalParentMatrix() {
        final Matrix originalMatrix = new Matrix();
        originalMatrix.setTranslate(10.0f, 20.0f);
        final CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
        builder.setMatrix(originalMatrix);

        final CursorAnchorInfo originalInstance = builder.build();

        assertEquals(originalMatrix, originalInstance.getMatrix());

        final Matrix additionalParentMatrix = new Matrix();
        additionalParentMatrix.setTranslate(1.0f, 2.0f);

        final Matrix newMatrix = new Matrix(originalMatrix);
        newMatrix.postConcat(additionalParentMatrix);

        builder.reset();
        builder.setMatrix(newMatrix);
        // An instance created by the standard Builder class.
        final CursorAnchorInfo newInstanceByBuilder = builder.build();

        // An instance created by an @hide method.
        final CursorAnchorInfo newInstanceByMethod =
                CursorAnchorInfo.createForAdditionalParentMatrix(
                        originalInstance, additionalParentMatrix);

        assertEquals(newMatrix, newInstanceByBuilder.getMatrix());
        assertEquals(newMatrix, newInstanceByMethod.getMatrix());
        assertEquals(newInstanceByBuilder.hashCode(), newInstanceByMethod.hashCode());
    }
}