Merge "DO NOT MERGE: Update HandwritingIme to show bounds information" into udc-dev

This commit is contained in:
Taran Singh
2023-04-12 21:10:47 +00:00
committed by Android (Google) Code Review
3 changed files with 220 additions and 11 deletions

View File

@@ -0,0 +1,116 @@
/*
* 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);
}
}
}

View File

@@ -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;
}
@@ -228,7 +229,7 @@ public class HandwritingIme extends InputMethodService {
mRichGestureModeSpinner = new Spinner(this);
mRichGestureModeSpinner.setPadding(100, 0, 100, 0);
mRichGestureModeSpinner.setTooltipText("Handwriting IME mode");
String[] items = new String[] {
String[] items = new String[]{
"Handwriting IME - Rich gesture disabled",
"Rich gesture SELECT",
"Rich gesture DELETE",
@@ -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);
}
}

View File

@@ -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;
}
}