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

Commit 8c482663 authored by Haoyu Zhang's avatar Haoyu Zhang Committed by Android (Google) Code Review
Browse files

Merge "Update HandwritingIme to show bounds information"

parents 91444a35 6709bf62
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;
    }
}