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

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

Merge "Fix that handwriting gesture not working if View is scaled" into main

parents ac310d16 9a67e3ca
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -267,3 +267,13 @@ flag {
    purpose: PURPOSE_BUGFIX
  }
}

flag {
  name: "handwriting_gesture_with_transformation"
  namespace: "text"
  description: "Fix handwriting gesture is not working when view has transformation."
  bug: "342619429"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}
+169 −43
Original line number Diff line number Diff line
@@ -28,10 +28,10 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_C
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import static android.view.inputmethod.EditorInfo.STYLUS_HANDWRITING_ENABLED_ANDROIDX_EXTRAS_KEY;
import static android.view.inputmethod.Flags.initiationWithoutInputConnection;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import static android.view.inputmethod.Flags.initiationWithoutInputConnection;
import android.R;
import android.annotation.CallSuper;
@@ -937,6 +937,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    private TextPaint mTempTextPaint;
    private Object mTempCursor;
    private Matrix mTempMatrix;
    @UnsupportedAppUsage
    private BoringLayout.Metrics mBoring;
@@ -12106,6 +12107,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    }
    private PointF convertFromScreenToContentCoordinates(PointF point) {
        if (Flags.handwritingGestureWithTransformation()) {
            if (mTempMatrix == null) {
                mTempMatrix = new Matrix();
            }
            Matrix matrix = mTempMatrix;
            matrix.reset();
            transformMatrixToLocal(matrix);
            matrix.postTranslate(
                    -viewportToContentHorizontalOffset(),
                    -viewportToContentVerticalOffset()
            );
            float[] copy = new float[] { point.x, point.y };
            matrix.mapPoints(copy);
            return new PointF(copy[0], copy[1]);
        }
        int[] screenToViewport = getLocationOnScreen();
        PointF copy = new PointF(point);
        copy.offset(
@@ -12115,6 +12132,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    }
    private RectF convertFromScreenToContentCoordinates(RectF rect) {
        if (Flags.handwritingGestureWithTransformation()) {
            if (mTempMatrix == null) {
                mTempMatrix = new Matrix();
            }
            Matrix matrix = mTempMatrix;
            matrix.reset();
            transformMatrixToLocal(matrix);
            matrix.postTranslate(
                    -viewportToContentHorizontalOffset(),
                    -viewportToContentVerticalOffset()
            );
            RectF copy = new RectF(rect);
            matrix.mapRect(copy);
            return copy;
        }
        int[] screenToViewport = getLocationOnScreen();
        RectF copy = new RectF(rect);
        copy.offset(
@@ -14279,6 +14312,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    }
    /**
     * Don't use, it returns wrong result when the view is scaled. This method can be removed once
     * Flags.handwritingGestureWithTransformation is enabled.
     * Assume
     * Helper method to set {@code rect} to this TextView's non-clipped area in its own coordinates.
     * This method obtains the view's visible rectangle whereas the method
     * {@link #getContentVisibleRect} returns the text layout's visible rectangle.
@@ -14299,6 +14335,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    }
    /**
     * Don't use, it returns wrong result when view is scaled. This method can be removed once
     * Flags.handwritingGestureWithTransformation is enabled.
     * Helper method to set {@code rect} to the text content's non-clipped area in the view's
     * coordinates.
     *
@@ -14314,6 +14352,58 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                getWidth() - getCompoundPaddingRight(), getHeight() - getCompoundPaddingBottom());
    }
    private boolean getEditorAndHandwritingBounds(@NonNull RectF editorBounds,
            @Nullable RectF handwritingBounds) {
        if (mTempRect == null) {
            mTempRect = new Rect();
        }
        Rect rect = mTempRect;
        if (!getGlobalVisibleRect(rect)) {
            return false;
        }
        if (mTempMatrix == null) {
            mTempMatrix = new Matrix();
        }
        Matrix matrix = mTempMatrix;
        matrix.reset();
        transformMatrixToLocal(matrix);
        editorBounds.set(rect);
        // When the view has transformations like scaleX/scaleY computing the global visible
        // rectangle will already apply the transformations. The getLocalVisibleRect only offsets
        // the global rectangle to local. And the result is wrong the View is scaled.
        //
        // This approach use the local transformation matrix to map the global rectangle to
        // local instead.
        //
        // Note: it doesn't work well with rotation. Because Rect must be
        // axis-aligned, when a rotated Rect becomes quadrilateral, the quadrilateral's
        // bounding box is stored at Rect instead. It makes the returned Rect larger than
        // the correct size.
        matrix.mapRect(editorBounds);
        if (handwritingBounds != null) {
            // Similar to editorBounds, handwritingBounds must be computed in global coordinates
            // and then converted back to local coordinates. Otherwise, if the view is scaled,
            // the handwritingBoundsOffsets are also scaled, which is not the expected behavior.
            handwritingBounds.top = rect.top -  getHandwritingBoundsOffsetTop();
            handwritingBounds.left = rect.left - getHandwritingBoundsOffsetLeft();
            handwritingBounds.bottom = rect.bottom + getHandwritingBoundsOffsetBottom();
            handwritingBounds.right = rect.right + getHandwritingBoundsOffsetRight();
            matrix.mapRect(handwritingBounds);
        }
        return true;
    }
    private boolean getContentVisibleRect(RectF rect) {
        if (!getEditorAndHandwritingBounds(rect, /* handwritingBounds= */null)) {
            return false;
        }
        // Clip the view's visible rect with the text layout's visible rect.
        return rect.intersect(getCompoundPaddingLeft(), getCompoundPaddingTop(),
                getWidth() - getCompoundPaddingRight(), getHeight() - getCompoundPaddingBottom());
    }
    /**
     * Populate requested character bounds in a {@link CursorAnchorInfo.Builder}
     *
@@ -14333,9 +14423,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            // character bounds in this case yet.
            return;
        }
        final RectF visibleRect = new RectF();
        if (Flags.handwritingGestureWithTransformation()) {
            getContentVisibleRect(visibleRect);
        } else {
            final Rect rect = new Rect();
            getContentVisibleRect(rect);
        final RectF visibleRect = new RectF(rect);
            visibleRect.set(rect);
        }
        final float[] characterBounds = getCharacterBounds(startIndex, endIndex,
                viewportToContentHorizontalOffset, viewportToContentVerticalOffset);
@@ -14438,24 +14534,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        builder.setMatrix(viewToScreenMatrix);
        if (includeEditorBounds) {
            final RectF editorBounds = new RectF();
            final RectF handwritingBounds = new RectF();
            if (Flags.handwritingGestureWithTransformation()) {
                getEditorAndHandwritingBounds(editorBounds, handwritingBounds);
            } else {
                if (mTempRect == null) {
                    mTempRect = new Rect();
                }
                final Rect bounds = mTempRect;
            final RectF editorBounds;
            final RectF handwritingBounds;
                // If the editor is not visible at all, return empty rectangles. We still need to
                // return an EditorBoundsInfo because IME has subscribed the EditorBoundsInfo.
                if (getViewVisibleRect(bounds)) {
                editorBounds = new RectF(bounds);
                handwritingBounds = new RectF(editorBounds);
                    editorBounds.set(bounds);
                    handwritingBounds.set(editorBounds);
                    handwritingBounds.top -= getHandwritingBoundsOffsetTop();
                    handwritingBounds.left -= getHandwritingBoundsOffsetLeft();
                    handwritingBounds.bottom += getHandwritingBoundsOffsetBottom();
                    handwritingBounds.right += getHandwritingBoundsOffsetRight();
            } else {
                // The editor is not visible at all, return empty rectangles. We still need to
                // return an EditorBoundsInfo because IME has subscribed the EditorBoundsInfo.
                editorBounds = new RectF();
                handwritingBounds = new RectF();
                }
            }
            EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
            EditorBoundsInfo editorBoundsInfo = boundsBuilder.setEditorBounds(editorBounds)
@@ -14533,6 +14631,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            }
            if (includeVisibleLineBounds) {
                if (Flags.handwritingGestureWithTransformation()) {
                    RectF visibleRect = new RectF();
                    if (getContentVisibleRect(visibleRect)) {
                        // Subtract the viewportToContentVerticalOffset to convert the view
                        // coordinates to layout coordinates.
                        final float visibleTop =
                                visibleRect.top - viewportToContentVerticalOffset;
                        final float visibleBottom =
                                visibleRect.bottom - viewportToContentVerticalOffset;
                        final int firstLine =
                                layout.getLineForVertical((int) Math.floor(visibleTop));
                        final int lastLine =
                                layout.getLineForVertical((int) Math.ceil(visibleBottom));
                        for (int line = firstLine; line <= lastLine; ++line) {
                            final float left = layout.getLineLeft(line)
                                    + viewportToContentHorizontalOffset;
                            final float top = layout.getLineTop(line)
                                    + viewportToContentVerticalOffset;
                            final float right = layout.getLineRight(line)
                                    + viewportToContentHorizontalOffset;
                            final float bottom = layout.getLineBottom(line, false)
                                    + viewportToContentVerticalOffset;
                            builder.addVisibleLineBounds(left, top, right, bottom);
                        }
                    }
                } else {
                    final Rect visibleRect = new Rect();
                    if (getContentVisibleRect(visibleRect)) {
                        // Subtract the viewportToContentVerticalOffset to convert the view
@@ -14560,6 +14685,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    }
                }
            }
        }
        if (includeTextAppearance) {
            builder.setTextAppearanceInfo(TextAppearanceInfo.createFromTextView(this));