summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2018-03-22 23:21:54 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-03-22 23:21:54 +0000
commit02c8d1b07fc5841029173ef9396ead24ef96ac2c (patch)
treebe87eb70a38ccf854bdb8a4f76d231f65d1980b5
parent5f8939eecdf0101919e01cab1bce1b9e05757ad1 (diff)
parenta553477ddf55d170a66410ed325ae5e5d3005965 (diff)
Merge "Make PrecomputedText Spannable for supporting selection" into pi-dev
-rw-r--r--api/current.txt4
-rw-r--r--core/java/android/text/DynamicLayout.java7
-rw-r--r--core/java/android/text/MeasuredParagraph.java16
-rw-r--r--core/java/android/text/PrecomputedText.java61
-rw-r--r--core/java/android/widget/TextView.java9
-rw-r--r--core/jni/android_text_MeasuredParagraph.cpp29
6 files changed, 115 insertions, 11 deletions
diff --git a/api/current.txt b/api/current.txt
index 6ea38cca9c12..4d4bf7416fe4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -44056,7 +44056,7 @@ package android.text {
method public abstract int getSpanTypeId();
}
- public class PrecomputedText implements android.text.Spanned {
+ public class PrecomputedText implements android.text.Spannable {
method public char charAt(int);
method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
method public int getParagraphCount();
@@ -44070,6 +44070,8 @@ package android.text {
method public java.lang.CharSequence getText();
method public int length();
method public int nextSpanTransition(int, int, java.lang.Class);
+ method public void removeSpan(java.lang.Object);
+ method public void setSpan(java.lang.Object, int, int, int);
method public java.lang.CharSequence subSequence(int, int);
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 18431cacbfaf..febca7ec9de1 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -704,7 +704,12 @@ public class DynamicLayout extends Layout {
// Spans other than ReplacementSpan can be ignored because line top and bottom are
// disjunction of all tops and bottoms, although it's not optimal.
final Paint paint = getPaint();
- paint.getTextBounds(text, start, end, mTempRect);
+ if (text instanceof PrecomputedText) {
+ PrecomputedText precomputed = (PrecomputedText) text;
+ precomputed.getBounds(start, end, mTempRect);
+ } else {
+ paint.getTextBounds(text, start, end, mTempRect);
+ }
final Paint.FontMetricsInt fm = paint.getFontMetricsInt();
return mTempRect.top < fm.top || mTempRect.bottom > fm.bottom;
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 980f47043269..a107cab58966 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -21,6 +21,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.text.AutoGrowArray.ByteArray;
import android.text.AutoGrowArray.FloatArray;
import android.text.AutoGrowArray.IntArray;
@@ -297,6 +298,18 @@ public class MeasuredParagraph {
}
/**
+ * Retrieves the bounding rectangle that encloses all of the characters, with an implied origin
+ * at (0, 0).
+ *
+ * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ */
+ public void getBounds(@NonNull Paint paint, @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end, @NonNull Rect bounds) {
+ nGetBounds(mNativePtr, mCopiedBuffer, paint.getNativeInstance(), start, end,
+ paint.getBidiFlags(), bounds);
+ }
+
+ /**
* Generates new MeasuredParagraph for Bidi computation.
*
* If recycle is null, this returns new instance. If recycle is not null, this fills computed
@@ -728,4 +741,7 @@ public class MeasuredParagraph {
@CriticalNative
private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+ private static native void nGetBounds(long nativePtr, char[] buf, long paintPtr, int start,
+ int end, int bidiFlag, Rect rect);
}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 9458184ee888..413df05d86ee 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -19,6 +19,8 @@ package android.text;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.text.style.MetricAffectingSpan;
import com.android.internal.util.Preconditions;
@@ -57,10 +59,12 @@ import java.util.Objects;
* </pre>
*
* Note that the {@link PrecomputedText} created from different parameters of the target
- * {@link android.widget.TextView} will be rejected internally and compute the text layout again
- * with the current {@link android.widget.TextView} parameters.
+ * {@link android.widget.TextView} will be rejected.
+ *
+ * Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to
+ * PrecomputedText.
*/
-public class PrecomputedText implements Spanned {
+public class PrecomputedText implements Spannable {
private static final char LINE_FEED = '\n';
/**
@@ -283,7 +287,7 @@ public class PrecomputedText implements Spanned {
// The original text.
- private final @NonNull SpannedString mText;
+ private final @NonNull SpannableString mText;
// The inclusive start offset of the measuring target.
private final @IntRange(from = 0) int mStart;
@@ -304,6 +308,9 @@ public class PrecomputedText implements Spanned {
* presented can save work on the UI thread.
* </p>
*
+ * Note that any {@link android.text.NoCopySpan} attached to the text won't be passed to the
+ * created PrecomputedText.
+ *
* @param text the text to be measured
* @param params parameters that define how text will be precomputed
* @return A {@link PrecomputedText}
@@ -347,7 +354,7 @@ public class PrecomputedText implements Spanned {
private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
@IntRange(from = 0) int end, @NonNull Params params,
@NonNull ParagraphInfo[] paraInfo) {
- mText = new SpannedString(text);
+ mText = new SpannableString(text, true /* ignoreNoCopySpan */);
mStart = start;
mEnd = end;
mParams = params;
@@ -457,6 +464,21 @@ public class PrecomputedText implements Spanned {
return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
}
+ /** @hide */
+ public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @NonNull Rect bounds) {
+ final int paraIndex = findParaIndex(start);
+ final int paraStart = getParagraphStart(paraIndex);
+ final int paraEnd = getParagraphEnd(paraIndex);
+ if (start < paraStart || paraEnd < end) {
+ throw new RuntimeException("Cannot measured across the paragraph:"
+ + "para: (" + paraStart + ", " + paraEnd + "), "
+ + "request: (" + start + ", " + end + ")");
+ }
+ getMeasuredParagraph(paraIndex).getBounds(mParams.mPaint,
+ start - paraStart, end - paraStart, bounds);
+ }
+
/**
* Returns the size of native PrecomputedText memory usage.
*
@@ -472,6 +494,35 @@ public class PrecomputedText implements Spanned {
}
///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spannable overrides
+ //
+ // Do not allow to modify MetricAffectingSpan
+
+ /**
+ * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+ */
+ @Override
+ public void setSpan(Object what, int start, int end, int flags) {
+ if (what instanceof MetricAffectingSpan) {
+ throw new IllegalArgumentException(
+ "MetricAffectingSpan can not be set to PrecomputedText.");
+ }
+ mText.setSpan(what, start, end, flags);
+ }
+
+ /**
+ * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+ */
+ @Override
+ public void removeSpan(Object what) {
+ if (what instanceof MetricAffectingSpan) {
+ throw new IllegalArgumentException(
+ "MetricAffectingSpan can not be removed from PrecomputedText.");
+ }
+ mText.removeSpan(what);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
// Spanned overrides
//
// Just proxy for underlying mText if appropriate.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f21c0b92add2..c366a9129d34 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5640,6 +5640,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
needEditableForNotification = true;
}
+ PrecomputedText precomputed =
+ (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
if (type == BufferType.EDITABLE || getKeyListener() != null
|| needEditableForNotification) {
createEditorIfNeeded();
@@ -5649,10 +5651,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setFilters(t, mFilters);
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) imm.restartInput(this);
- } else if (type == BufferType.SPANNABLE || mMovement != null) {
- text = mSpannableFactory.newSpannable(text);
- } else if (text instanceof PrecomputedText) {
- PrecomputedText precomputed = (PrecomputedText) text;
+ } else if (precomputed != null) {
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
@@ -5665,6 +5664,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
+ "PrecomputedText: " + precomputed.getParams()
+ "TextView: " + getTextMetricsParams());
}
+ } else if (type == BufferType.SPANNABLE || mMovement != null) {
+ text = mSpannableFactory.newSpannable(text);
} else if (!(text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index d33337dabae0..9d794175de18 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "MeasuredParagraph"
+#include "GraphicsJNI.h"
#include "ScopedIcuLocale.h"
#include "unicode/locid.h"
#include "unicode/brkiter.h"
@@ -109,6 +110,33 @@ static jfloat nGetWidth(jlong ptr, jint start, jint end) {
return r;
}
+// Regular JNI
+static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jlong paintPtr,
+ jint start, jint end, jint bidiFlags, jobject bounds) {
+ ScopedCharArrayRO text(env, javaText);
+ const minikin::U16StringPiece textBuffer(text.get(), text.size());
+
+ minikin::MeasuredText* mt = toMeasuredParagraph(ptr);
+ Paint* paint = toPaint(paintPtr);
+ const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
+ minikin::Layout layout = MinikinUtils::doLayout(paint,
+ static_cast<minikin::Bidi>(bidiFlags), typeface, textBuffer.data(), start, end - start,
+ textBuffer.size(), mt);
+
+ minikin::MinikinRect rect;
+ layout.getBounds(&rect);
+
+ SkRect r;
+ r.fLeft = rect.mLeft;
+ r.fTop = rect.mTop;
+ r.fRight = rect.mRight;
+ r.fBottom = rect.mBottom;
+
+ SkIRect ir;
+ r.roundOut(&ir);
+ GraphicsJNI::irect_to_jrect(ir, env, bounds);
+}
+
// CriticalNative
static jlong nGetReleaseFunc() {
return toJLong(&releaseMeasuredParagraph);
@@ -128,6 +156,7 @@ static const JNINativeMethod gMethods[] = {
// MeasuredParagraph native functions.
{"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives
+ {"nGetBounds", "(J[CJIIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
{"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native
};