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

Commit 9299d14a authored by Haoyu Zhang's avatar Haoyu Zhang Committed by Taran Singh
Browse files

DO NOT MERGE: Update HandwritingIme to show bounds information

Bug: 217957587
Test: manually tested
Change-Id: Id0ae8a133901b26c1a04143d061a460e76c7bc4b
(cherry picked from commit 6709bf62)
parent e5d57e99
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.google.android.test.handwritingime;

import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_EDITOR_BOUNDS;
import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_NONE;
import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_VISIBLE_LINE_BOUNDS;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorBoundsInfo;

import androidx.annotation.Nullable;

import com.android.internal.graphics.ColorUtils;

import java.util.List;

public class BoundsInfoDrawHelper {
    private static final Paint sPaint = new Paint();
    private static final int EDITOR_BOUNDS_COLOR =
            ColorUtils.setAlphaComponent(Color.DKGRAY, 128);
    private static final int HANDWRITING_BOUNDS_COLOR =
            ColorUtils.setAlphaComponent(Color.BLUE, 128);
    private static final int VISIBLE_LINE_BOUNDS_COLOR =
            ColorUtils.setAlphaComponent(Color.MAGENTA, 128);

    public static void draw(Canvas canvas, View inkView, int boundsInfoMode,
            CursorAnchorInfo cursorAnchorInfo) {
        if (boundsInfoMode == BOUNDS_INFO_NONE || cursorAnchorInfo == null) {
            return;
        }

        // The matrix in CursorAnchorInfo transforms the editor coordinates to on-screen
        // coordinates. We then transform the matrix from the on-screen coordinates to the
        // inkView's coordinates. So the result matrix transforms the editor coordinates
        // to the inkView coordinates.
        final Matrix matrix = cursorAnchorInfo.getMatrix();
        inkView.transformMatrixToLocal(matrix);

        if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
            drawEditorBoundsInfo(canvas, matrix, cursorAnchorInfo.getEditorBoundsInfo());
        }

        if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
            drawVisibleLineBounds(canvas, matrix, cursorAnchorInfo.getVisibleLineBounds());
        }
    }

    private static void setPaintForEditorBoundsInfo() {
        sPaint.reset();
        sPaint.setStyle(Paint.Style.STROKE);
        sPaint.setStrokeWidth(5f);
    }

    private static void drawEditorBoundsInfo(Canvas canvas, Matrix matrix,
            @Nullable EditorBoundsInfo editorBoundsInfo) {
        if (editorBoundsInfo == null) {
            return;
        }
        final RectF editorBounds = editorBoundsInfo.getEditorBounds();
        setPaintForEditorBoundsInfo();
        if (editorBounds != null) {
            final RectF localEditorBounds = new RectF(editorBounds);
            matrix.mapRect(localEditorBounds);
            sPaint.setColor(EDITOR_BOUNDS_COLOR);
            canvas.drawRect(localEditorBounds, sPaint);
        }

        final RectF handwritingBounds = editorBoundsInfo.getHandwritingBounds();
        if (handwritingBounds != null) {
            final RectF localHandwritingBounds = new RectF(handwritingBounds);
            matrix.mapRect(localHandwritingBounds);
            sPaint.setColor(HANDWRITING_BOUNDS_COLOR);
            canvas.drawRect(localHandwritingBounds, sPaint);
        }
    }

    private static void setPaintForVisibleLineBounds() {
        sPaint.reset();
        sPaint.setStyle(Paint.Style.STROKE);
        sPaint.setStrokeWidth(2f);
        sPaint.setColor(VISIBLE_LINE_BOUNDS_COLOR);
    }

    private static void drawVisibleLineBounds(Canvas canvas, Matrix matrix,
            List<RectF> visibleLineBounds) {
        if (visibleLineBounds.isEmpty()) {
            return;
        }
        setPaintForVisibleLineBounds();
        for (RectF lineBound : visibleLineBounds) {
            matrix.mapRect(lineBound);
            canvas.drawRect(lineBound, sPaint);
        }
    }
}
+88 −11
Original line number Diff line number Diff line
@@ -25,7 +25,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InsertGesture;
@@ -34,6 +36,7 @@ import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Spinner;
@@ -43,9 +46,6 @@ import java.util.Random;
import java.util.function.IntConsumer;

public class HandwritingIme extends InputMethodService {

    public static final int HEIGHT_DP = 100;

    private static final int OP_NONE = 0;
    private static final int OP_SELECT = 1;
    private static final int OP_DELETE = 2;
@@ -62,6 +62,12 @@ public class HandwritingIme extends InputMethodService {
    private Spinner mRichGestureGranularitySpinner;
    private PointF mRichGestureStartPoint;

    static final int BOUNDS_INFO_NONE = 0;
    static final int BOUNDS_INFO_VISIBLE_LINE_BOUNDS = 1;
    static final int BOUNDS_INFO_EDITOR_BOUNDS = 2;
    private int mBoundsInfoMode = BOUNDS_INFO_NONE;
    private LinearLayout mBoundsInfoCheckBoxes;

    private final IntConsumer mResultConsumer = value -> Log.d(TAG, "Gesture result: " + value);

    interface HandwritingFinisher {
@@ -201,12 +207,7 @@ public class HandwritingIme extends InputMethodService {
    public View onCreateInputView() {
        Log.d(TAG, "onCreateInputView");
        final ViewGroup view = new FrameLayout(this);
        final View inner = new View(this);
        final float density = getResources().getDisplayMetrics().density;
        final int height = (int) (HEIGHT_DP * density);
        view.setPadding(0, 0, 0, 0);
        view.addView(inner, new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT, height));

        LinearLayout layout = new LinearLayout(this);
        layout.setLayoutParams(new LinearLayout.LayoutParams(
@@ -214,9 +215,9 @@ public class HandwritingIme extends InputMethodService {
        layout.setOrientation(LinearLayout.VERTICAL);
        layout.addView(getRichGestureActionsSpinner());
        layout.addView(getRichGestureGranularitySpinner());

        layout.addView(getBoundsInfoCheckBoxes());
        layout.setBackgroundColor(getColor(R.color.holo_green_light));
        view.addView(layout);
        inner.setBackgroundColor(getColor(R.color.holo_green_light));

        return view;
    }
@@ -259,6 +260,69 @@ public class HandwritingIme extends InputMethodService {
        return mRichGestureModeSpinner;
    }

    private void updateCursorAnchorInfo(int boundsInfoMode) {
        final InputConnection ic = getCurrentInputConnection();
        if (ic == null) return;

        if (boundsInfoMode == BOUNDS_INFO_NONE) {
            ic.requestCursorUpdates(0);
            return;
        }

        final int cursorUpdateMode = InputConnection.CURSOR_UPDATE_MONITOR;
        int cursorUpdateFilter = 0;
        if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
            cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS;
        }

        if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
            cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS;
        }
        ic.requestCursorUpdates(cursorUpdateMode | cursorUpdateFilter);
    }

    private void updateBoundsInfoMode() {
        if (mInk != null) {
            mInk.setBoundsInfoMode(mBoundsInfoMode);
        }
        updateCursorAnchorInfo(mBoundsInfoMode);
    }

    private View getBoundsInfoCheckBoxes() {
        if (mBoundsInfoCheckBoxes != null) {
            return mBoundsInfoCheckBoxes;
        }
        mBoundsInfoCheckBoxes = new LinearLayout(this);
        mBoundsInfoCheckBoxes.setPadding(100, 0, 100, 0);
        mBoundsInfoCheckBoxes.setOrientation(LinearLayout.HORIZONTAL);

        final CheckBox editorBoundsInfoCheckBox = new CheckBox(this);
        editorBoundsInfoCheckBox.setText("EditorBoundsInfo");
        editorBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                mBoundsInfoMode |= BOUNDS_INFO_EDITOR_BOUNDS;
            } else {
                mBoundsInfoMode &= ~BOUNDS_INFO_EDITOR_BOUNDS;
            }
            updateBoundsInfoMode();
        });

        final CheckBox visibleLineBoundsInfoCheckBox = new CheckBox(this);
        visibleLineBoundsInfoCheckBox.setText("VisibleLineBounds");
        visibleLineBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                mBoundsInfoMode |= BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
            } else {
                mBoundsInfoMode &= ~BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
            }
            updateBoundsInfoMode();
        });

        mBoundsInfoCheckBoxes.addView(editorBoundsInfoCheckBox);
        mBoundsInfoCheckBoxes.addView(visibleLineBoundsInfoCheckBox);
        return mBoundsInfoCheckBoxes;
    }

    private View getRichGestureGranularitySpinner() {
        if (mRichGestureGranularitySpinner != null) {
            return mRichGestureGranularitySpinner;
@@ -294,6 +358,7 @@ public class HandwritingIme extends InputMethodService {
        Log.d(TAG, "onPrepareStylusHandwriting ");
        if (mInk == null) {
            mInk = new InkView(this, new HandwritingFinisherImpl(), new StylusConsumer());
            mInk.setBoundsInfoMode(mBoundsInfoMode);
        }
    }

@@ -323,4 +388,16 @@ public class HandwritingIme extends InputMethodService {
    private boolean areRichGesturesEnabled() {
        return mRichGestureMode != OP_NONE;
    }

    @Override
    public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
        if (mInk != null) {
            mInk.setCursorAnchorInfo(cursorAnchorInfo);
        }
    }

    @Override
    public void onStartInput(EditorInfo attribute, boolean restarting) {
        updateCursorAnchorInfo(mBoundsInfoMode);
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.inputmethod.CursorAnchorInfo;

class InkView extends View {
    private static final long FINISH_TIMEOUT = 1500;
@@ -37,6 +38,9 @@ class InkView extends View {
    private static final float STYLUS_MOVE_TOLERANCE = 1;
    private Runnable mFinishRunnable;

    private CursorAnchorInfo mCursorAnchorInfo;
    private int mBoundsInfoMode;

    InkView(Context context, HandwritingIme.HandwritingFinisher hwController,
            HandwritingIme.StylusConsumer consumer) {
        super(context);
@@ -66,6 +70,7 @@ class InkView extends View {

        canvas.drawPath(mPath, mPaint);
        canvas.drawARGB(20, 255, 50, 50);
        BoundsInfoDrawHelper.draw(canvas, this, mBoundsInfoMode, mCursorAnchorInfo);
    }

    private void stylusStart(float x, float y) {
@@ -156,4 +161,15 @@ class InkView extends View {
        return mFinishRunnable;
    }

    void setCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
        mCursorAnchorInfo = cursorAnchorInfo;
        invalidate();
    }

    void setBoundsInfoMode(int boundsInfoMode) {
        if (boundsInfoMode != mBoundsInfoMode) {
            invalidate();
        }
        mBoundsInfoMode = boundsInfoMode;
    }
}