Merge "Support InsertModeGesture"
This commit is contained in:
@@ -73,10 +73,12 @@ import android.text.Spanned;
|
||||
import android.text.SpannedString;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.InsertModeTransformationMethod;
|
||||
import android.text.method.KeyListener;
|
||||
import android.text.method.MetaKeyKeyListener;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.text.method.OffsetMapping;
|
||||
import android.text.method.TransformationMethod;
|
||||
import android.text.method.WordIterator;
|
||||
import android.text.style.EasyEditSpan;
|
||||
import android.text.style.SuggestionRangeSpan;
|
||||
@@ -136,6 +138,7 @@ import android.window.OnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.graphics.ColorUtils;
|
||||
import com.android.internal.inputmethod.EditableInputConnection;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
@@ -461,6 +464,7 @@ public class Editor {
|
||||
private int mLineChangeSlopMin;
|
||||
|
||||
private final AccessibilitySmartActions mA11ySmartActions;
|
||||
private InsertModeController mInsertModeController;
|
||||
|
||||
Editor(TextView textView) {
|
||||
mTextView = textView;
|
||||
@@ -2109,6 +2113,10 @@ public class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
if (mInsertModeController != null) {
|
||||
mInsertModeController.onDraw(canvas);
|
||||
}
|
||||
|
||||
if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {
|
||||
drawHardwareAccelerated(canvas, layout, highlightPaths, highlightPaints,
|
||||
selectionHighlight, selectionHighlightPaint, cursorOffsetVertical);
|
||||
@@ -8054,6 +8062,110 @@ public class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InsertModeController {
|
||||
private final TextView mTextView;
|
||||
private boolean mIsInsertModeActive;
|
||||
private InsertModeTransformationMethod mInsertModeTransformationMethod;
|
||||
private final Paint mHighlightPaint;
|
||||
|
||||
InsertModeController(@NonNull TextView textView) {
|
||||
mTextView = Objects.requireNonNull(textView);
|
||||
mIsInsertModeActive = false;
|
||||
mInsertModeTransformationMethod = null;
|
||||
mHighlightPaint = new Paint();
|
||||
|
||||
// The highlight color is supposed to be 12% of the color primary40. We can't
|
||||
// directly access Material 3 theme. But because Material 3 sets the colorPrimary to
|
||||
// be primary40, here we hardcoded it to be 12% of colorPrimary.
|
||||
final TypedValue typedValue = new TypedValue();
|
||||
mTextView.getContext().getTheme()
|
||||
.resolveAttribute(R.attr.colorPrimary, typedValue, true);
|
||||
final int colorPrimary = typedValue.data;
|
||||
final int highlightColor = ColorUtils.setAlphaComponent(colorPrimary,
|
||||
(int) (0.12f * Color.alpha(colorPrimary)));
|
||||
mHighlightPaint.setColor(highlightColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter insert mode.
|
||||
* @param offset the index to set the cursor.
|
||||
* @return true if the call is successful. false if a) it's already in the insert mode,
|
||||
* b) it failed to enter the insert mode.
|
||||
*/
|
||||
boolean enterInsertMode(int offset) {
|
||||
if (mIsInsertModeActive) return false;
|
||||
|
||||
TransformationMethod oldTransformationMethod =
|
||||
mTextView.getTransformationMethod();
|
||||
if (oldTransformationMethod instanceof OffsetMapping) {
|
||||
// We can't support the case where the oldTransformationMethod is an OffsetMapping.
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean isSingleLine = mTextView.isSingleLine();
|
||||
mInsertModeTransformationMethod = new InsertModeTransformationMethod(offset,
|
||||
isSingleLine, oldTransformationMethod);
|
||||
mTextView.setTransformationMethod(mInsertModeTransformationMethod);
|
||||
Selection.setSelection((Spannable) mTextView.getText(), offset);
|
||||
|
||||
mIsInsertModeActive = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void exitInsertMode() {
|
||||
if (!mIsInsertModeActive) return;
|
||||
if (mInsertModeTransformationMethod == null
|
||||
|| mInsertModeTransformationMethod != mTextView.getTransformationMethod()) {
|
||||
// If mInsertionModeTransformationMethod doesn't match the one on TextView,
|
||||
// something else have changed the TextView's TransformationMethod while the
|
||||
// insertion mode is active. We don't need to restore the oldTransformationMethod.
|
||||
// TODO(265871733): support the case where setTransformationMethod is called in
|
||||
// the insert mode.
|
||||
mIsInsertModeActive = false;
|
||||
return;
|
||||
}
|
||||
// Changing TransformationMethod will reset selection range to [0, 0), we need to
|
||||
// manually restore the old selection range.
|
||||
final int selectionStart = mTextView.getSelectionStart();
|
||||
final int selectionEnd = mTextView.getSelectionEnd();
|
||||
final TransformationMethod oldTransformationMethod =
|
||||
mInsertModeTransformationMethod.getOldTransformationMethod();
|
||||
mTextView.setTransformationMethod(oldTransformationMethod);
|
||||
Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
|
||||
mIsInsertModeActive = false;
|
||||
}
|
||||
|
||||
void onDraw(Canvas canvas) {
|
||||
if (!mIsInsertModeActive) return;
|
||||
final CharSequence transformedText = mTextView.getTransformed();
|
||||
if (transformedText instanceof InsertModeTransformationMethod.TransformedText) {
|
||||
final Layout layout = mTextView.getLayout();
|
||||
if (layout == null) return;
|
||||
final InsertModeTransformationMethod.TransformedText insertModeTransformedText =
|
||||
((InsertModeTransformationMethod.TransformedText) transformedText);
|
||||
final int highlightStart = insertModeTransformedText.getHighlightStart();
|
||||
final int highlightEnd = insertModeTransformedText.getHighlightEnd();
|
||||
final Layout.SelectionRectangleConsumer consumer =
|
||||
(left, top, right, bottom, textSelectionLayout) ->
|
||||
canvas.drawRect(left, top, right, bottom, mHighlightPaint);
|
||||
layout.getSelection(highlightStart, highlightEnd, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean enterInsertMode(int offset) {
|
||||
if (mInsertModeController == null) {
|
||||
if (mTextView == null) return false;
|
||||
mInsertModeController = new InsertModeController(mTextView);
|
||||
}
|
||||
return mInsertModeController.enterInsertMode(offset);
|
||||
}
|
||||
|
||||
void exitInsertMode() {
|
||||
if (mInsertModeController == null) return;
|
||||
mInsertModeController.exitInsertMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the nodeInfo with smart actions.
|
||||
*/
|
||||
|
||||
@@ -202,6 +202,7 @@ import android.view.inputmethod.HandwritingGesture;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.InsertGesture;
|
||||
import android.view.inputmethod.InsertModeGesture;
|
||||
import android.view.inputmethod.JoinOrSplitGesture;
|
||||
import android.view.inputmethod.PreviewableHandwritingGesture;
|
||||
import android.view.inputmethod.RemoveSpaceGesture;
|
||||
@@ -2534,7 +2535,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
public CharSequence getTransformed() {
|
||||
return mTransformed;
|
||||
}
|
||||
@@ -9620,6 +9621,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
gestures.add(InsertGesture.class);
|
||||
gestures.add(RemoveSpaceGesture.class);
|
||||
gestures.add(JoinOrSplitGesture.class);
|
||||
gestures.add(InsertModeGesture.class);
|
||||
outAttrs.setSupportedHandwritingGestures(gestures);
|
||||
|
||||
Set<Class<? extends PreviewableHandwritingGesture>> previews = new ArraySet<>();
|
||||
@@ -10169,6 +10171,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public int performHandwritingInsertModeGesture(@NonNull InsertModeGesture gesture) {
|
||||
final PointF insertPoint =
|
||||
convertFromScreenToContentCoordinates(gesture.getInsertionPoint());
|
||||
final int line = getLineForHandwritingGesture(insertPoint);
|
||||
final CancellationSignal cancellationSignal = gesture.getCancellationSignal();
|
||||
|
||||
// If no cancellationSignal is provided, don't enter the insert mode.
|
||||
if (line == -1 || cancellationSignal == null) {
|
||||
return handleGestureFailure(gesture);
|
||||
}
|
||||
|
||||
final int offset = mLayout.getOffsetForHorizontal(line, insertPoint.x);
|
||||
|
||||
if (!mEditor.enterInsertMode(offset)) {
|
||||
return InputConnection.HANDWRITING_GESTURE_RESULT_FAILED;
|
||||
}
|
||||
cancellationSignal.setOnCancelListener(() -> mEditor.exitInsertMode());
|
||||
return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private int handleGestureFailure(HandwritingGesture gesture) {
|
||||
return handleGestureFailure(gesture, /* isPreview= */ false);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.HandwritingGesture;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InsertGesture;
|
||||
import android.view.inputmethod.InsertModeGesture;
|
||||
import android.view.inputmethod.JoinOrSplitGesture;
|
||||
import android.view.inputmethod.PreviewableHandwritingGesture;
|
||||
import android.view.inputmethod.RemoveSpaceGesture;
|
||||
@@ -314,6 +315,8 @@ public final class EditableInputConnection extends BaseInputConnection
|
||||
result = mTextView.performHandwritingRemoveSpaceGesture((RemoveSpaceGesture) gesture);
|
||||
} else if (gesture instanceof JoinOrSplitGesture) {
|
||||
result = mTextView.performHandwritingJoinOrSplitGesture((JoinOrSplitGesture) gesture);
|
||||
} else if (gesture instanceof InsertModeGesture) {
|
||||
result = mTextView.performHandwritingInsertModeGesture((InsertModeGesture) gesture);
|
||||
} else {
|
||||
result = HANDWRITING_GESTURE_RESULT_UNSUPPORTED;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user