summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Justin Ghan <justinghan@google.com> 2022-09-20 17:14:28 -0700
committer Justin Ghan <justinghan@google.com> 2022-09-23 01:06:09 -0700
commite51ef4024c7ad9683b4feaa42f175d68d166dd5d (patch)
treeff2c860189947a7daa661aadf88a9d290351aee5
parentefb6b0e65c62616d09d7fe884e938f1fe06b57ef (diff)
Delete gesture: remove extra whitespace around deleted text
Bug: 243983058 Test: atest android.widget.cts.TextViewHandwritingGestureTest Change-Id: I759bbb99e94a373390c77cb28fb71aa641c63560
-rw-r--r--core/java/android/text/TextUtils.java26
-rw-r--r--core/java/android/text/method/WordIterator.java36
-rw-r--r--core/java/android/widget/TextView.java55
3 files changed, 88 insertions, 29 deletions
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 51e36657e769..596e4914896a 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -93,7 +93,9 @@ public class TextUtils {
private static final String ELLIPSIS_NORMAL = "\u2026"; // HORIZONTAL ELLIPSIS (…)
private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // TWO DOT LEADER (‥)
- private static final int LINE_FEED_CODE_POINT = 10;
+ /** @hide */
+ public static final int LINE_FEED_CODE_POINT = 10;
+
private static final int NBSP_CODE_POINT = 160;
/**
@@ -2335,11 +2337,29 @@ public class TextUtils {
|| codePoint == LINE_FEED_CODE_POINT;
}
- private static boolean isWhiteSpace(int codePoint) {
+ /** @hide */
+ public static boolean isWhitespace(int codePoint) {
return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
}
/** @hide */
+ public static boolean isWhitespaceExceptNewline(int codePoint) {
+ return isWhitespace(codePoint) && !isNewline(codePoint);
+ }
+
+ /** @hide */
+ public static boolean isPunctuation(int codePoint) {
+ int type = Character.getType(codePoint);
+ return type == Character.CONNECTOR_PUNCTUATION
+ || type == Character.DASH_PUNCTUATION
+ || type == Character.END_PUNCTUATION
+ || type == Character.FINAL_QUOTE_PUNCTUATION
+ || type == Character.INITIAL_QUOTE_PUNCTUATION
+ || type == Character.OTHER_PUNCTUATION
+ || type == Character.START_PUNCTUATION;
+ }
+
+ /** @hide */
@Nullable
public static String withoutPrefix(@Nullable String prefix, @Nullable String str) {
if (prefix == null || str == null) return str;
@@ -2430,7 +2450,7 @@ public class TextUtils {
gettingCleaned.removeRange(offset, offset + codePointLen);
} else if (type == Character.CONTROL && !isNewline) {
gettingCleaned.removeRange(offset, offset + codePointLen);
- } else if (trim && !isWhiteSpace(codePoint)) {
+ } else if (trim && !isWhitespace(codePoint)) {
// This is only executed if the code point is not removed
if (firstNonWhiteSpace == -1) {
firstNonWhiteSpace = offset;
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index f427e1bd2b72..6d18d2c541b6 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -24,6 +24,7 @@ import android.icu.text.BreakIterator;
import android.os.Build;
import android.text.CharSequenceCharacterIterator;
import android.text.Selection;
+import android.text.TextUtils;
import java.util.Locale;
@@ -275,9 +276,9 @@ public class WordIterator implements Selection.PositionIterator {
}
/**
- * If <code>offset</code> is within a group of punctuation as defined
- * by {@link #isPunctuation(int)}, returns the index of the first character
- * of that group, otherwise returns BreakIterator.DONE.
+ * If <code>offset</code> is within a group of punctuation as defined by {@link
+ * TextUtils#isPunctuation(int)}, returns the index of the first character of that group,
+ * otherwise returns BreakIterator.DONE.
*
* @param offset the offset to search from.
*/
@@ -292,9 +293,9 @@ public class WordIterator implements Selection.PositionIterator {
}
/**
- * If <code>offset</code> is within a group of punctuation as defined
- * by {@link #isPunctuation(int)}, returns the index of the last character
- * of that group plus one, otherwise returns BreakIterator.DONE.
+ * If <code>offset</code> is within a group of punctuation as defined by {@link
+ * TextUtils#isPunctuation(int)}, returns the index of the last character of that group plus
+ * one, otherwise returns BreakIterator.DONE.
*
* @param offset the offset to search from.
*/
@@ -309,8 +310,8 @@ public class WordIterator implements Selection.PositionIterator {
}
/**
- * Indicates if the provided offset is after a punctuation character
- * as defined by {@link #isPunctuation(int)}.
+ * Indicates if the provided offset is after a punctuation character as defined by {@link
+ * TextUtils#isPunctuation(int)}.
*
* @param offset the offset to check from.
* @return Whether the offset is after a punctuation character.
@@ -319,14 +320,14 @@ public class WordIterator implements Selection.PositionIterator {
public boolean isAfterPunctuation(int offset) {
if (mStart < offset && offset <= mEnd) {
final int codePoint = Character.codePointBefore(mCharSeq, offset);
- return isPunctuation(codePoint);
+ return TextUtils.isPunctuation(codePoint);
}
return false;
}
/**
- * Indicates if the provided offset is at a punctuation character
- * as defined by {@link #isPunctuation(int)}.
+ * Indicates if the provided offset is at a punctuation character as defined by {@link
+ * TextUtils#isPunctuation(int)}.
*
* @param offset the offset to check from.
* @return Whether the offset is at a punctuation character.
@@ -335,7 +336,7 @@ public class WordIterator implements Selection.PositionIterator {
public boolean isOnPunctuation(int offset) {
if (mStart <= offset && offset < mEnd) {
final int codePoint = Character.codePointAt(mCharSeq, offset);
- return isPunctuation(codePoint);
+ return TextUtils.isPunctuation(codePoint);
}
return false;
}
@@ -369,17 +370,6 @@ public class WordIterator implements Selection.PositionIterator {
return !isOnPunctuation(offset) && isAfterPunctuation(offset);
}
- private static boolean isPunctuation(int cp) {
- final int type = Character.getType(cp);
- return (type == Character.CONNECTOR_PUNCTUATION
- || type == Character.DASH_PUNCTUATION
- || type == Character.END_PUNCTUATION
- || type == Character.FINAL_QUOTE_PUNCTUATION
- || type == Character.INITIAL_QUOTE_PUNCTUATION
- || type == Character.OTHER_PUNCTUATION
- || type == Character.START_PUNCTUATION);
- }
-
private boolean isAfterLetterOrDigit(int offset) {
if (mStart < offset && offset <= mEnd) {
final int codePoint = Character.codePointBefore(mCharSeq, offset);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 41d00a267c59..38c8801803ef 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9330,9 +9330,58 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (range == null) {
return handleGestureFailure(gesture);
}
- getEditableText().delete(range[0], range[1]);
- Selection.setSelection(getEditableText(), range[0]);
- // TODO(b/243983058): Delete extra spaces.
+ int start = range[0];
+ int end = range[1];
+
+ // For word granularity, adjust the start and end offsets to remove extra whitespace around
+ // the deleted text.
+ if (gesture.getGranularity() == HandwritingGesture.GRANULARITY_WORD) {
+ // If the deleted text is at the start of the text, the behavior is the same as the case
+ // where the deleted text follows a new line character.
+ int codePointBeforeStart = start > 0
+ ? Character.codePointBefore(mText, start) : TextUtils.LINE_FEED_CODE_POINT;
+ // If the deleted text is at the end of the text, the behavior is the same as the case
+ // where the deleted text precedes a new line character.
+ int codePointAtEnd = end < mText.length()
+ ? Character.codePointAt(mText, end) : TextUtils.LINE_FEED_CODE_POINT;
+ if (TextUtils.isWhitespaceExceptNewline(codePointBeforeStart)
+ && (TextUtils.isWhitespace(codePointAtEnd)
+ || TextUtils.isPunctuation(codePointAtEnd))) {
+ // Remove whitespace (except new lines) before the deleted text, in these cases:
+ // - There is whitespace following the deleted text
+ // e.g. "one [deleted] three" -> "one | three" -> "one| three"
+ // - There is punctuation following the deleted text
+ // e.g. "one [deleted]!" -> "one |!" -> "one|!"
+ // - There is a new line following the deleted text
+ // e.g. "one [deleted]\n" -> "one |\n" -> "one|\n"
+ // - The deleted text is at the end of the text
+ // e.g. "one [deleted]" -> "one |" -> "one|"
+ // (The pipe | indicates the cursor position.)
+ while (start > 0 && TextUtils.isWhitespaceExceptNewline(codePointBeforeStart)) {
+ start -= Character.charCount(codePointBeforeStart);
+ codePointBeforeStart = Character.codePointBefore(mText, start);
+ }
+ } else if (TextUtils.isWhitespaceExceptNewline(codePointAtEnd)
+ && (TextUtils.isWhitespace(codePointBeforeStart)
+ || TextUtils.isPunctuation(codePointBeforeStart))) {
+ // Remove whitespace (except new lines) after the deleted text, in these cases:
+ // - There is punctuation preceding the deleted text
+ // e.g. "([deleted] two)" -> "(| two)" -> "(|two)"
+ // - There is a new line preceding the deleted text
+ // e.g. "\n[deleted] two" -> "\n| two" -> "\n|two"
+ // - The deleted text is at the start of the text
+ // e.g. "[deleted] two" -> "| two" -> "|two"
+ // (The pipe | indicates the cursor position.)
+ while (end < mText.length()
+ && TextUtils.isWhitespaceExceptNewline(codePointAtEnd)) {
+ end += Character.charCount(codePointAtEnd);
+ codePointAtEnd = Character.codePointAt(mText, end);
+ }
+ }
+ }
+
+ getEditableText().delete(start, end);
+ Selection.setSelection(getEditableText(), start);
return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
}