412 lines
16 KiB
Java
412 lines
16 KiB
Java
package androidx.core.text;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.os.Build;
|
|
import android.text.Layout;
|
|
import android.text.PrecomputedText;
|
|
import android.text.Spannable;
|
|
import android.text.SpannableString;
|
|
import android.text.StaticLayout;
|
|
import android.text.TextDirectionHeuristic;
|
|
import android.text.TextDirectionHeuristics;
|
|
import android.text.TextPaint;
|
|
import android.text.TextUtils;
|
|
import android.text.style.MetricAffectingSpan;
|
|
import androidx.annotation.GuardedBy;
|
|
import androidx.annotation.IntRange;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RequiresApi;
|
|
import androidx.annotation.RestrictTo;
|
|
import androidx.annotation.UiThread;
|
|
import androidx.core.os.TraceCompat;
|
|
import androidx.core.util.ObjectsCompat;
|
|
import androidx.core.util.Preconditions;
|
|
import c.d.b.a.a;
|
|
import java.util.ArrayList;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.Future;
|
|
import java.util.concurrent.FutureTask;
|
|
public class PrecomputedTextCompat implements Spannable {
|
|
private static final char LINE_FEED = '\n';
|
|
@NonNull
|
|
@GuardedBy("sLock")
|
|
private static Executor sExecutor;
|
|
private static final Object sLock = new Object();
|
|
@NonNull
|
|
private final int[] mParagraphEnds;
|
|
@NonNull
|
|
private final Params mParams;
|
|
@NonNull
|
|
private final Spannable mText;
|
|
@Nullable
|
|
private final PrecomputedText mWrapped;
|
|
|
|
public static final class Params {
|
|
private final int mBreakStrategy;
|
|
private final int mHyphenationFrequency;
|
|
@NonNull
|
|
private final TextPaint mPaint;
|
|
@Nullable
|
|
private final TextDirectionHeuristic mTextDir;
|
|
public final PrecomputedText.Params mWrapped;
|
|
|
|
public static class Builder {
|
|
private int mBreakStrategy;
|
|
private int mHyphenationFrequency;
|
|
@NonNull
|
|
private final TextPaint mPaint;
|
|
private TextDirectionHeuristic mTextDir;
|
|
|
|
public Builder(@NonNull TextPaint textPaint) {
|
|
this.mPaint = textPaint;
|
|
if (Build.VERSION.SDK_INT >= 23) {
|
|
this.mBreakStrategy = 1;
|
|
this.mHyphenationFrequency = 1;
|
|
} else {
|
|
this.mHyphenationFrequency = 0;
|
|
this.mBreakStrategy = 0;
|
|
}
|
|
this.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
|
|
}
|
|
|
|
@NonNull
|
|
public Params build() {
|
|
return new Params(this.mPaint, this.mTextDir, this.mBreakStrategy, this.mHyphenationFrequency);
|
|
}
|
|
|
|
@RequiresApi(23)
|
|
public Builder setBreakStrategy(int i) {
|
|
this.mBreakStrategy = i;
|
|
return this;
|
|
}
|
|
|
|
@RequiresApi(23)
|
|
public Builder setHyphenationFrequency(int i) {
|
|
this.mHyphenationFrequency = i;
|
|
return this;
|
|
}
|
|
|
|
@RequiresApi(18)
|
|
public Builder setTextDirection(@NonNull TextDirectionHeuristic textDirectionHeuristic) {
|
|
this.mTextDir = textDirectionHeuristic;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
@RequiresApi(28)
|
|
public Params(@NonNull PrecomputedText.Params params) {
|
|
this.mPaint = params.getTextPaint();
|
|
this.mTextDir = params.getTextDirection();
|
|
this.mBreakStrategy = params.getBreakStrategy();
|
|
this.mHyphenationFrequency = params.getHyphenationFrequency();
|
|
this.mWrapped = Build.VERSION.SDK_INT < 29 ? null : params;
|
|
}
|
|
|
|
@SuppressLint({"NewApi"})
|
|
public Params(@NonNull TextPaint textPaint, @NonNull TextDirectionHeuristic textDirectionHeuristic, int i, int i2) {
|
|
if (Build.VERSION.SDK_INT >= 29) {
|
|
this.mWrapped = new PrecomputedText.Params.Builder(textPaint).setBreakStrategy(i).setHyphenationFrequency(i2).setTextDirection(textDirectionHeuristic).build();
|
|
} else {
|
|
this.mWrapped = null;
|
|
}
|
|
this.mPaint = textPaint;
|
|
this.mTextDir = textDirectionHeuristic;
|
|
this.mBreakStrategy = i;
|
|
this.mHyphenationFrequency = i2;
|
|
}
|
|
|
|
public boolean equals(@Nullable Object obj) {
|
|
if (obj == this) {
|
|
return true;
|
|
}
|
|
if (!(obj instanceof Params)) {
|
|
return false;
|
|
}
|
|
Params params = (Params) obj;
|
|
return equalsWithoutTextDirection(params) && this.mTextDir == params.getTextDirection();
|
|
}
|
|
|
|
@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX})
|
|
public boolean equalsWithoutTextDirection(@NonNull Params params) {
|
|
int i = Build.VERSION.SDK_INT;
|
|
if ((i >= 23 && (this.mBreakStrategy != params.getBreakStrategy() || this.mHyphenationFrequency != params.getHyphenationFrequency())) || this.mPaint.getTextSize() != params.getTextPaint().getTextSize() || this.mPaint.getTextScaleX() != params.getTextPaint().getTextScaleX() || this.mPaint.getTextSkewX() != params.getTextPaint().getTextSkewX() || this.mPaint.getLetterSpacing() != params.getTextPaint().getLetterSpacing() || !TextUtils.equals(this.mPaint.getFontFeatureSettings(), params.getTextPaint().getFontFeatureSettings()) || this.mPaint.getFlags() != params.getTextPaint().getFlags()) {
|
|
return false;
|
|
}
|
|
if (i >= 24) {
|
|
if (!this.mPaint.getTextLocales().equals(params.getTextPaint().getTextLocales())) {
|
|
return false;
|
|
}
|
|
} else if (!this.mPaint.getTextLocale().equals(params.getTextPaint().getTextLocale())) {
|
|
return false;
|
|
}
|
|
return this.mPaint.getTypeface() == null ? params.getTextPaint().getTypeface() == null : this.mPaint.getTypeface().equals(params.getTextPaint().getTypeface());
|
|
}
|
|
|
|
@RequiresApi(23)
|
|
public int getBreakStrategy() {
|
|
return this.mBreakStrategy;
|
|
}
|
|
|
|
@RequiresApi(23)
|
|
public int getHyphenationFrequency() {
|
|
return this.mHyphenationFrequency;
|
|
}
|
|
|
|
@Nullable
|
|
@RequiresApi(18)
|
|
public TextDirectionHeuristic getTextDirection() {
|
|
return this.mTextDir;
|
|
}
|
|
|
|
@NonNull
|
|
public TextPaint getTextPaint() {
|
|
return this.mPaint;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return Build.VERSION.SDK_INT >= 24 ? ObjectsCompat.hash(Float.valueOf(this.mPaint.getTextSize()), Float.valueOf(this.mPaint.getTextScaleX()), Float.valueOf(this.mPaint.getTextSkewX()), Float.valueOf(this.mPaint.getLetterSpacing()), Integer.valueOf(this.mPaint.getFlags()), this.mPaint.getTextLocales(), this.mPaint.getTypeface(), Boolean.valueOf(this.mPaint.isElegantTextHeight()), this.mTextDir, Integer.valueOf(this.mBreakStrategy), Integer.valueOf(this.mHyphenationFrequency)) : ObjectsCompat.hash(Float.valueOf(this.mPaint.getTextSize()), Float.valueOf(this.mPaint.getTextScaleX()), Float.valueOf(this.mPaint.getTextSkewX()), Float.valueOf(this.mPaint.getLetterSpacing()), Integer.valueOf(this.mPaint.getFlags()), this.mPaint.getTextLocale(), this.mPaint.getTypeface(), Boolean.valueOf(this.mPaint.isElegantTextHeight()), this.mTextDir, Integer.valueOf(this.mBreakStrategy), Integer.valueOf(this.mHyphenationFrequency));
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder("{");
|
|
StringBuilder L = a.L("textSize=");
|
|
L.append(this.mPaint.getTextSize());
|
|
sb.append(L.toString());
|
|
sb.append(", textScaleX=" + this.mPaint.getTextScaleX());
|
|
sb.append(", textSkewX=" + this.mPaint.getTextSkewX());
|
|
int i = Build.VERSION.SDK_INT;
|
|
StringBuilder L2 = a.L(", letterSpacing=");
|
|
L2.append(this.mPaint.getLetterSpacing());
|
|
sb.append(L2.toString());
|
|
sb.append(", elegantTextHeight=" + this.mPaint.isElegantTextHeight());
|
|
if (i >= 24) {
|
|
StringBuilder L3 = a.L(", textLocale=");
|
|
L3.append(this.mPaint.getTextLocales());
|
|
sb.append(L3.toString());
|
|
} else {
|
|
StringBuilder L4 = a.L(", textLocale=");
|
|
L4.append(this.mPaint.getTextLocale());
|
|
sb.append(L4.toString());
|
|
}
|
|
StringBuilder L5 = a.L(", typeface=");
|
|
L5.append(this.mPaint.getTypeface());
|
|
sb.append(L5.toString());
|
|
if (i >= 26) {
|
|
StringBuilder L6 = a.L(", variationSettings=");
|
|
L6.append(this.mPaint.getFontVariationSettings());
|
|
sb.append(L6.toString());
|
|
}
|
|
StringBuilder L7 = a.L(", textDir=");
|
|
L7.append(this.mTextDir);
|
|
sb.append(L7.toString());
|
|
sb.append(", breakStrategy=" + this.mBreakStrategy);
|
|
sb.append(", hyphenationFrequency=" + this.mHyphenationFrequency);
|
|
sb.append("}");
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
public static class PrecomputedTextFutureTask extends FutureTask<PrecomputedTextCompat> {
|
|
|
|
public static class PrecomputedTextCallback implements Callable<PrecomputedTextCompat> {
|
|
private Params mParams;
|
|
private CharSequence mText;
|
|
|
|
public PrecomputedTextCallback(@NonNull Params params, @NonNull CharSequence charSequence) {
|
|
this.mParams = params;
|
|
this.mText = charSequence;
|
|
}
|
|
|
|
@Override // java.util.concurrent.Callable
|
|
public PrecomputedTextCompat call() throws Exception {
|
|
return PrecomputedTextCompat.create(this.mText, this.mParams);
|
|
}
|
|
}
|
|
|
|
public PrecomputedTextFutureTask(@NonNull Params params, @NonNull CharSequence charSequence) {
|
|
super(new PrecomputedTextCallback(params, charSequence));
|
|
}
|
|
}
|
|
|
|
@RequiresApi(28)
|
|
private PrecomputedTextCompat(@NonNull PrecomputedText precomputedText, @NonNull Params params) {
|
|
this.mText = precomputedText;
|
|
this.mParams = params;
|
|
this.mParagraphEnds = null;
|
|
this.mWrapped = Build.VERSION.SDK_INT < 29 ? null : precomputedText;
|
|
}
|
|
|
|
private PrecomputedTextCompat(@NonNull CharSequence charSequence, @NonNull Params params, @NonNull int[] iArr) {
|
|
this.mText = new SpannableString(charSequence);
|
|
this.mParams = params;
|
|
this.mParagraphEnds = iArr;
|
|
this.mWrapped = null;
|
|
}
|
|
|
|
@SuppressLint({"NewApi"})
|
|
public static PrecomputedTextCompat create(@NonNull CharSequence charSequence, @NonNull Params params) {
|
|
PrecomputedText.Params params2;
|
|
Preconditions.checkNotNull(charSequence);
|
|
Preconditions.checkNotNull(params);
|
|
try {
|
|
TraceCompat.beginSection("PrecomputedText");
|
|
if (Build.VERSION.SDK_INT >= 29 && (params2 = params.mWrapped) != null) {
|
|
return new PrecomputedTextCompat(PrecomputedText.create(charSequence, params2), params);
|
|
}
|
|
ArrayList arrayList = new ArrayList();
|
|
int length = charSequence.length();
|
|
int i = 0;
|
|
while (i < length) {
|
|
int indexOf = TextUtils.indexOf(charSequence, '\n', i, length);
|
|
i = indexOf < 0 ? length : indexOf + 1;
|
|
arrayList.add(Integer.valueOf(i));
|
|
}
|
|
int[] iArr = new int[arrayList.size()];
|
|
for (int i2 = 0; i2 < arrayList.size(); i2++) {
|
|
iArr[i2] = ((Integer) arrayList.get(i2)).intValue();
|
|
}
|
|
if (Build.VERSION.SDK_INT >= 23) {
|
|
StaticLayout.Builder.obtain(charSequence, 0, charSequence.length(), params.getTextPaint(), Integer.MAX_VALUE).setBreakStrategy(params.getBreakStrategy()).setHyphenationFrequency(params.getHyphenationFrequency()).setTextDirection(params.getTextDirection()).build();
|
|
} else {
|
|
new StaticLayout(charSequence, params.getTextPaint(), Integer.MAX_VALUE, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
|
}
|
|
PrecomputedTextCompat precomputedTextCompat = new PrecomputedTextCompat(charSequence, params, iArr);
|
|
TraceCompat.endSection();
|
|
return precomputedTextCompat;
|
|
} finally {
|
|
TraceCompat.endSection();
|
|
}
|
|
}
|
|
|
|
@UiThread
|
|
public static Future<PrecomputedTextCompat> getTextFuture(@NonNull CharSequence charSequence, @NonNull Params params, @Nullable Executor executor) {
|
|
PrecomputedTextFutureTask precomputedTextFutureTask = new PrecomputedTextFutureTask(params, charSequence);
|
|
if (executor == null) {
|
|
synchronized (sLock) {
|
|
if (sExecutor == null) {
|
|
sExecutor = Executors.newFixedThreadPool(1);
|
|
}
|
|
executor = sExecutor;
|
|
}
|
|
}
|
|
executor.execute(precomputedTextFutureTask);
|
|
return precomputedTextFutureTask;
|
|
}
|
|
|
|
@Override // java.lang.CharSequence
|
|
public char charAt(int i) {
|
|
return this.mText.charAt(i);
|
|
}
|
|
|
|
@IntRange(from = 0)
|
|
@SuppressLint({"NewApi"})
|
|
public int getParagraphCount() {
|
|
return Build.VERSION.SDK_INT >= 29 ? this.mWrapped.getParagraphCount() : this.mParagraphEnds.length;
|
|
}
|
|
|
|
@IntRange(from = 0)
|
|
@SuppressLint({"NewApi"})
|
|
public int getParagraphEnd(@IntRange(from = 0) int i) {
|
|
Preconditions.checkArgumentInRange(i, 0, getParagraphCount(), "paraIndex");
|
|
return Build.VERSION.SDK_INT >= 29 ? this.mWrapped.getParagraphEnd(i) : this.mParagraphEnds[i];
|
|
}
|
|
|
|
@IntRange(from = 0)
|
|
@SuppressLint({"NewApi"})
|
|
public int getParagraphStart(@IntRange(from = 0) int i) {
|
|
Preconditions.checkArgumentInRange(i, 0, getParagraphCount(), "paraIndex");
|
|
if (Build.VERSION.SDK_INT >= 29) {
|
|
return this.mWrapped.getParagraphStart(i);
|
|
}
|
|
if (i == 0) {
|
|
return 0;
|
|
}
|
|
return this.mParagraphEnds[i - 1];
|
|
}
|
|
|
|
@NonNull
|
|
public Params getParams() {
|
|
return this.mParams;
|
|
}
|
|
|
|
@Nullable
|
|
@RequiresApi(28)
|
|
@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX})
|
|
public PrecomputedText getPrecomputedText() {
|
|
Spannable spannable = this.mText;
|
|
if (spannable instanceof PrecomputedText) {
|
|
return (PrecomputedText) spannable;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override // android.text.Spanned
|
|
public int getSpanEnd(Object obj) {
|
|
return this.mText.getSpanEnd(obj);
|
|
}
|
|
|
|
@Override // android.text.Spanned
|
|
public int getSpanFlags(Object obj) {
|
|
return this.mText.getSpanFlags(obj);
|
|
}
|
|
|
|
@Override // android.text.Spanned
|
|
public int getSpanStart(Object obj) {
|
|
return this.mText.getSpanStart(obj);
|
|
}
|
|
|
|
@Override // android.text.Spanned
|
|
@SuppressLint({"NewApi"})
|
|
public <T> T[] getSpans(int i, int i2, Class<T> cls) {
|
|
return Build.VERSION.SDK_INT >= 29 ? (T[]) this.mWrapped.getSpans(i, i2, cls) : (T[]) this.mText.getSpans(i, i2, cls);
|
|
}
|
|
|
|
@Override // java.lang.CharSequence
|
|
public int length() {
|
|
return this.mText.length();
|
|
}
|
|
|
|
@Override // android.text.Spanned
|
|
public int nextSpanTransition(int i, int i2, Class cls) {
|
|
return this.mText.nextSpanTransition(i, i2, cls);
|
|
}
|
|
|
|
@Override // android.text.Spannable
|
|
@SuppressLint({"NewApi"})
|
|
public void removeSpan(Object obj) {
|
|
if (obj instanceof MetricAffectingSpan) {
|
|
throw new IllegalArgumentException("MetricAffectingSpan can not be removed from PrecomputedText.");
|
|
} else if (Build.VERSION.SDK_INT >= 29) {
|
|
this.mWrapped.removeSpan(obj);
|
|
} else {
|
|
this.mText.removeSpan(obj);
|
|
}
|
|
}
|
|
|
|
@Override // android.text.Spannable
|
|
@SuppressLint({"NewApi"})
|
|
public void setSpan(Object obj, int i, int i2, int i3) {
|
|
if (obj instanceof MetricAffectingSpan) {
|
|
throw new IllegalArgumentException("MetricAffectingSpan can not be set to PrecomputedText.");
|
|
} else if (Build.VERSION.SDK_INT >= 29) {
|
|
this.mWrapped.setSpan(obj, i, i2, i3);
|
|
} else {
|
|
this.mText.setSpan(obj, i, i2, i3);
|
|
}
|
|
}
|
|
|
|
@Override // java.lang.CharSequence
|
|
public CharSequence subSequence(int i, int i2) {
|
|
return this.mText.subSequence(i, i2);
|
|
}
|
|
|
|
@Override // java.lang.CharSequence, java.lang.Object
|
|
@NonNull
|
|
public String toString() {
|
|
return this.mText.toString();
|
|
}
|
|
}
|