10b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka/* 20b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Copyright 2018 The Android Open Source Project 30b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 40b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License"); 50b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * you may not use this file except in compliance with the License. 60b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * You may obtain a copy of the License at 70b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 80b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * http://www.apache.org/licenses/LICENSE-2.0 90b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 100b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Unless required by applicable law or agreed to in writing, software 110b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS, 120b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * See the License for the specific language governing permissions and 140b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * limitations under the License. 150b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 160b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 170b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakapackage androidx.core.text; 180b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 190b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.os.Build; 200b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.Layout; 210b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.PrecomputedText; 220b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.Spannable; 230b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.SpannableString; 240b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.StaticLayout; 250b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.TextDirectionHeuristic; 260b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.TextDirectionHeuristics; 270b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.TextPaint; 280b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.TextUtils; 290b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport android.text.style.MetricAffectingSpan; 300b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 310b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport androidx.annotation.IntRange; 320b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport androidx.annotation.NonNull; 330b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport androidx.annotation.Nullable; 340b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport androidx.annotation.RequiresApi; 350b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport androidx.core.os.BuildCompat; 360b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport androidx.core.util.ObjectsCompat; 370b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport androidx.core.util.Preconditions; 380b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 390b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakaimport java.util.ArrayList; 400b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 410b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka/** 420b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * A text which has the character metrics data. 430b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 440b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * A text object that contains the character metrics data and can be used to improve the performance 450b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * of text layout operations. When a PrecomputedTextCompat is created with a given 460b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * {@link CharSequence}, it will measure the text metrics during the creation. This PrecomputedText 470b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * instance can be set on {@link android.widget.TextView} or {@link StaticLayout}. Since the text 480b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * layout information will be included in this instance, {@link android.widget.TextView} or 490b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * {@link StaticLayout} will not have to recalculate this information. 500b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 510b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * On API 28 or later, there is full PrecomputedText support by framework. From API 21 to API 27, 520b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * PrecomputedTextCompat relies on internal text layout cache. PrecomputedTextCompat immediately 530b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * computes the text layout in the constuctor to warm up the internal text layout cache. On API 20 540b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * or before, PrecomputedTextCompat does nothing. 550b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 560b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to 570b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * PrecomputedText. 580b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 590b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonakapublic class PrecomputedTextCompat implements Spannable { 600b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private static final char LINE_FEED = '\n'; 610b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 620b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 630b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * The information required for building {@link PrecomputedTextCompat}. 640b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 650b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Contains information required for precomputing text measurement metadata, so it can be done 660b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout 670b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * constraints are not known. 680b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 690b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public static final class Params { 700b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final @NonNull TextPaint mPaint; 710b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 720b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // null on API 17 or before, non null on API 18 or later. 730b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final @Nullable TextDirectionHeuristic mTextDir; 740b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 750b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final int mBreakStrategy; 760b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 770b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final int mHyphenationFrequency; 780b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 790b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final PrecomputedText.Params mWrapped; 800b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 810b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 820b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * A builder for creating {@link Params}. 830b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 840b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public static class Builder { 850b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // The TextPaint used for measurement. 860b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final @NonNull TextPaint mPaint; 870b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 880b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // The requested text direction. 890b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private TextDirectionHeuristic mTextDir; 900b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 910b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // The break strategy for this measured text. 920b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private int mBreakStrategy; 930b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 940b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // The hyphenation frequency for this measured text. 950b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private int mHyphenationFrequency; 960b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 970b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 980b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Builder constructor. 990b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1000b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @param paint the paint to be used for drawing 1010b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 1020b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public Builder(@NonNull TextPaint paint) { 1030b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint = paint; 1040b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 1050b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; 1060b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NORMAL; 1070b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 1080b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mBreakStrategy = mHyphenationFrequency = 0; 1090b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1100b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 1110b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; 1120b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 1130b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mTextDir = null; 1140b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1150b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1160b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 1170b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 1180b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Set the line break strategy. 1190b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1200b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. 1210b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1220b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * On API 22 and below, this has no effect as there is no line break strategy. 1230b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1240b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @param strategy the break strategy 1250b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return PrecomputedTextCompat.Builder instance 1260b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @see StaticLayout.Builder#setBreakStrategy 1270b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @see android.widget.TextView#setBreakStrategy 1280b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 1290b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(23) 1300b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public Builder setBreakStrategy(int strategy) { 1310b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mBreakStrategy = strategy; 1320b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return this; 1330b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1340b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 1350b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 1360b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Set the hyphenation frequency. 1370b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1380b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. 1390b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1400b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * On API 22 and below, this has no effect as there is no hyphenation frequency. 1410b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1420b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @param frequency the hyphenation frequency 1430b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return PrecomputedTextCompat.Builder instance 1440b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @see StaticLayout.Builder#setHyphenationFrequency 1450b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @see android.widget.TextView#setHyphenationFrequency 1460b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 1470b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(23) 1480b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public Builder setHyphenationFrequency(int frequency) { 1490b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mHyphenationFrequency = frequency; 1500b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return this; 1510b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1520b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 1530b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 1540b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Set the text direction heuristic. 1550b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1560b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. 1570b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1580b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * On API 17 or before, text direction heuristics cannot be modified, so this method 1590b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * does nothing. 1600b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1610b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @param textDir the text direction heuristic for resolving bidi behavior 1620b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return PrecomputedTextCompat.Builder instance 1630b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @see StaticLayout.Builder#setTextDirection 1640b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 1650b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(18) 1660b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) { 1670b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mTextDir = textDir; 1680b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return this; 1690b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1700b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 1710b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 1720b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Build the {@link Params}. 1730b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 1740b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return the layout parameter 1750b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 1760b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public @NonNull Params build() { 1770b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency); 1780b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1790b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1800b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 1810b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, 1820b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka int strategy, int frequency) { 1830b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP()) { 1840b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mWrapped = new PrecomputedText.Params.Builder(paint).setBreakStrategy(strategy) 1850b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka .setHyphenationFrequency(frequency).setTextDirection(textDir).build(); 1860b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 1870b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mWrapped = null; 1880b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1890b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint = paint; 1900b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mTextDir = textDir; 1910b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mBreakStrategy = strategy; 1920b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mHyphenationFrequency = frequency; 1930b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 1940b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 1950b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(28) 1960b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public Params(@NonNull PrecomputedText.Params wrapped) { 1970b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint = wrapped.getTextPaint(); 1980b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mTextDir = wrapped.getTextDirection(); 1990b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mBreakStrategy = wrapped.getBreakStrategy(); 2000b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mHyphenationFrequency = wrapped.getHyphenationFrequency(); 2010b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mWrapped = wrapped; 2020b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2030b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2040b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2050b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 2060b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the {@link TextPaint} for this text. 2070b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2080b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return A {@link TextPaint} 2090b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 2100b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public @NonNull TextPaint getTextPaint() { 2110b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mPaint; 2120b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2130b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2140b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 2150b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the {@link TextDirectionHeuristic} for this text. 2160b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2170b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * On API 17 and below, this returns null, otherwise returns non-null 2180b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * TextDirectionHeuristic. 2190b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2200b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return the {@link TextDirectionHeuristic} 2210b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 2220b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(18) 2230b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public @Nullable TextDirectionHeuristic getTextDirection() { 2240b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mTextDir; 2250b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2260b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2270b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 2280b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the break strategy for this text. 2290b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2300b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * On API 22 and below, this returns 0. 2310b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2320b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return the line break strategy 2330b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 2340b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(23) 2350b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int getBreakStrategy() { 2360b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mBreakStrategy; 2370b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2380b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2390b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 2400b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the hyphenation frequency for this text. 2410b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2420b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * On API 22 and below, this returns 0. 2430b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2440b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return the hyphenation frequency 2450b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 2460b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(23) 2470b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int getHyphenationFrequency() { 2480b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mHyphenationFrequency; 2490b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2500b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2510b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 2520b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Check if the same text layout. 2530b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 2540b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return true if this and the given param result in the same text layout 2550b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 2560b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 2570b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public boolean equals(@Nullable Object o) { 2580b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (o == this) { 2590b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return true; 2600b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2610b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (o == null || !(o instanceof Params)) { 2620b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2630b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2640b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka Params other = (Params) o; 2650b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mWrapped != null) { 2665d03935847b4129633cf65d41bbde384eb899d84Seigo Nonaka return mWrapped.equals(other.mWrapped); 2670b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2680b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2690b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 2700b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mBreakStrategy != other.getBreakStrategy()) { 2710b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2720b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2730b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mHyphenationFrequency != other.getHyphenationFrequency()) { 2740b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2750b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2760b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2770b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2780b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 2790b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mTextDir != other.getTextDirection()) { 2800b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2810b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2820b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2830b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 2840b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mPaint.getTextSize() != other.getTextPaint().getTextSize()) { 2850b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2860b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2870b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mPaint.getTextScaleX() != other.getTextPaint().getTextScaleX()) { 2880b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2890b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2900b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mPaint.getTextSkewX() != other.getTextPaint().getTextSkewX()) { 2910b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2920b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2930b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 2940b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mPaint.getLetterSpacing() != other.getTextPaint().getLetterSpacing()) { 2950b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 2960b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 2970b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (!TextUtils.equals(mPaint.getFontFeatureSettings(), 2980b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka other.getTextPaint().getFontFeatureSettings())) { 2990b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 3000b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3010b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3020b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mPaint.getFlags() != other.getTextPaint().getFlags()) { 3030b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 3040b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3050b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 3060b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (!mPaint.getTextLocales().equals(other.getTextPaint().getTextLocales())) { 3070b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 3080b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3090b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 3100b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (!mPaint.getTextLocale().equals(other.getTextPaint().getTextLocale())) { 3110b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 3120b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3130b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3140b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (mPaint.getTypeface() == null) { 3150b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (other.getTextPaint().getTypeface() != null) { 3160b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 3170b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3180b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else if (!mPaint.getTypeface().equals(other.getTextPaint().getTypeface())) { 3190b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return false; 3200b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3210b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3220b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return true; 3230b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3240b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3250b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 3260b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int hashCode() { 3270b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 3280b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return ObjectsCompat.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), 3290b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTextSkewX(), mPaint.getLetterSpacing(), mPaint.getFlags(), 3300b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.isElegantTextHeight(), 3310b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mTextDir, mBreakStrategy, mHyphenationFrequency); 3320b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 3330b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return ObjectsCompat.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), 3340b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTextSkewX(), mPaint.getLetterSpacing(), mPaint.getFlags(), 3350b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTextLocale(), mPaint.getTypeface(), mPaint.isElegantTextHeight(), 3360b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mTextDir, mBreakStrategy, mHyphenationFrequency); 3370b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 3380b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return ObjectsCompat.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), 3390b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTextSkewX(), mPaint.getFlags(), mPaint.getTextLocale(), 3400b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTypeface(), mTextDir, mBreakStrategy, mHyphenationFrequency); 3410b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 3420b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return ObjectsCompat.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), 3430b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTextSkewX(), mPaint.getFlags(), mPaint.getTextLocale(), 3440b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTypeface(), mTextDir, mBreakStrategy, mHyphenationFrequency); 3450b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 3460b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return ObjectsCompat.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), 3470b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mPaint.getTextSkewX(), mPaint.getFlags(), mPaint.getTypeface(), mTextDir, 3480b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mBreakStrategy, mHyphenationFrequency); 3490b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3500b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3510b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3520b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 3530b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public String toString() { 3540b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka StringBuilder sb = new StringBuilder("{"); 3550b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append("textSize=" + mPaint.getTextSize()); 3560b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", textScaleX=" + mPaint.getTextScaleX()); 3570b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", textSkewX=" + mPaint.getTextSkewX()); 3580b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 3590b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", letterSpacing=" + mPaint.getLetterSpacing()); 3600b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", elegantTextHeight=" + mPaint.isElegantTextHeight()); 3610b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3620b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 3630b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", textLocale=" + mPaint.getTextLocales()); 3640b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 3650b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", textLocale=" + mPaint.getTextLocale()); 3660b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3670b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", typeface=" + mPaint.getTypeface()); 3680b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 3690b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", variationSettings=" + mPaint.getFontVariationSettings()); 3700b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3710b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", textDir=" + mTextDir); 3720b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", breakStrategy=" + mBreakStrategy); 3730b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append(", hyphenationFrequency=" + mHyphenationFrequency); 3740b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka sb.append("}"); 3750b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return sb.toString(); 3760b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 3770b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka }; 3780b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3790b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // The original text. 3800b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final @NonNull Spannable mText; 3810b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3820b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final @NonNull Params mParams; 3830b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3840b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // The list of measured paragraph info. 3850b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final @NonNull int[] mParagraphEnds; 3860b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3870b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // null on API 27 or before. Non-null on API 28 or later 3880b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private final @Nullable PrecomputedText mWrapped; 3890b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 3900b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 3910b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph 3920b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * positioning information. 3930b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * <p> 3940b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * This can be expensive, so computing this on a background thread before your text will be 3950b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * presented can save work on the UI thread. 3960b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * </p> 3970b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 3980b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Note that any {@link android.text.NoCopySpan} attached to the text won't be passed to the 3990b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * created PrecomputedText. 4000b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * 4010b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @param text the text to be measured 4020b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @param params parameters that define how text will be precomputed 4030b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @return A {@link PrecomputedText} 4040b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 4050b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public static PrecomputedTextCompat create(@NonNull CharSequence text, @NonNull Params params) { 4060b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka Preconditions.checkNotNull(text); 4070b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka Preconditions.checkNotNull(params); 4080b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4090b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP() && params.mWrapped != null) { 4100b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return new PrecomputedTextCompat(PrecomputedText.create(text, params.mWrapped), params); 4110b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4120b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4130b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka ArrayList<Integer> ends = new ArrayList<>(); 4140b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4150b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka int paraEnd = 0; 4160b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka int end = text.length(); 4170b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka for (int paraStart = 0; paraStart < end; paraStart = paraEnd) { 4180b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); 4190b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (paraEnd < 0) { 4200b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph 4210b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // end. 4220b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka paraEnd = end; 4230b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 4240b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. 4250b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4260b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4270b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka ends.add(paraEnd); 4280b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4290b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka int[] result = new int[ends.size()]; 4300b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka for (int i = 0; i < ends.size(); ++i) { 4310b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka result[i] = ends.get(i); 4320b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4330b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4340b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // No framework support for PrecomputedText 4350b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Compute text layout and throw away StaticLayout for the purpose of warming up the 4360b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // internal text layout cache. 4370b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 4380b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka StaticLayout.Builder.obtain(text, 0, text.length(), params.getTextPaint(), 4390b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka Integer.MAX_VALUE) 4400b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka .setBreakStrategy(params.getBreakStrategy()) 4410b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka .setHyphenationFrequency(params.getHyphenationFrequency()) 4420b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka .setTextDirection(params.getTextDirection()) 4430b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka .build(); 4440b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 4450b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka new StaticLayout(text, params.getTextPaint(), Integer.MAX_VALUE, 4460b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); 4470b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 4480b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // There is no way of precomputing text layout on API 20 or before 4490b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Do nothing 4500b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4510b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4520b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return new PrecomputedTextCompat(text, params, result); 4530b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4540b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4550b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Use PrecomputedText.create instead. 4560b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private PrecomputedTextCompat(@NonNull CharSequence text, @NonNull Params params, 4570b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @NonNull int[] paraEnds) { 4580b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mText = new SpannableString(text); 4590b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mParams = params; 4600b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mParagraphEnds = paraEnds; 4610b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mWrapped = null; 4620b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4630b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4640b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @RequiresApi(28) 4650b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private PrecomputedTextCompat(@NonNull PrecomputedText precomputed, @NonNull Params params) { 4660b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mText = precomputed; 4670b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mParams = params; 4680b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mParagraphEnds = null; 4690b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mWrapped = precomputed; 4700b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4710b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4720b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 4730b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the layout parameters used to measure this text. 4740b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 4750b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public @NonNull Params getParams() { 4760b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mParams; 4770b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4780b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4790b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 4800b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the count of paragraphs. 4810b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 4820b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public @IntRange(from = 0) int getParagraphCount() { 4830b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP()) { 4840b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mWrapped.getParagraphCount(); 4850b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 4860b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mParagraphEnds.length; 4870b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4880b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 4890b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 4900b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 4910b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the paragraph start offset of the text. 4920b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 4930b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) { 4940b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); 4950b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP()) { 4960b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mWrapped.getParagraphStart(paraIndex); 4970b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 4980b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return paraIndex == 0 ? 0 : mParagraphEnds[paraIndex - 1]; 4990b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5000b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5010b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5020b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 5030b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * Returns the paragraph end offset of the text. 5040b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 5050b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) { 5060b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); 5070b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP()) { 5085d03935847b4129633cf65d41bbde384eb899d84Seigo Nonaka return mWrapped.getParagraphEnd(paraIndex); 5090b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 5100b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mParagraphEnds[paraIndex]; 5110b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5120b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5130b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5140b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5150b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka private int findParaIndex(@IntRange(from = 0) int pos) { 5160b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka for (int i = 0; i < mParagraphEnds.length; ++i) { 5170b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (pos < mParagraphEnds[i]) { 5180b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return i; 5190b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5200b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5210b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka throw new IndexOutOfBoundsException( 5220b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka "pos must be less than " + mParagraphEnds[mParagraphEnds.length - 1] 5230b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka + ", gave " + pos); 5240b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5250b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5260b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /////////////////////////////////////////////////////////////////////////////////////////////// 5270b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Spannable overrides 5280b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // 5290b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Do not allow to modify MetricAffectingSpan 5300b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5310b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 5320b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified. 5330b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 5340b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 5350b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public void setSpan(Object what, int start, int end, int flags) { 5360b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (what instanceof MetricAffectingSpan) { 5370b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka throw new IllegalArgumentException( 5380b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka "MetricAffectingSpan can not be set to PrecomputedText."); 5390b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5400b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP()) { 5410b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mWrapped.setSpan(what, start, end, flags); 5420b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 5430b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mText.setSpan(what, start, end, flags); 5440b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5450b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5460b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5470b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /** 5480b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified. 5490b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka */ 5500b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 5510b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public void removeSpan(Object what) { 5520b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (what instanceof MetricAffectingSpan) { 5530b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka throw new IllegalArgumentException( 5540b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka "MetricAffectingSpan can not be removed from PrecomputedText."); 5550b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5560b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP()) { 5570b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mWrapped.removeSpan(what); 5580b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 5590b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka mText.removeSpan(what); 5600b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5610b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5620b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5630b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /////////////////////////////////////////////////////////////////////////////////////////////// 5640b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Spanned overrides 5650b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // 5660b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Just proxy for underlying mText if appropriate. 5670b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5680b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 5690b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public <T> T[] getSpans(int start, int end, Class<T> type) { 5700b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka if (BuildCompat.isAtLeastP()) { 5710b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mWrapped.getSpans(start, end, type); 5720b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } else { 5730b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.getSpans(start, end, type); 5740b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5750b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5760b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5770b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5780b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 5790b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int getSpanStart(Object tag) { 5800b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.getSpanStart(tag); 5810b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5820b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5830b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 5840b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int getSpanEnd(Object tag) { 5850b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.getSpanEnd(tag); 5860b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5870b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5880b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 5890b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int getSpanFlags(Object tag) { 5900b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.getSpanFlags(tag); 5910b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5920b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5930b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 5940b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int nextSpanTransition(int start, int limit, Class type) { 5950b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.nextSpanTransition(start, limit, type); 5960b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 5970b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 5980b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka /////////////////////////////////////////////////////////////////////////////////////////////// 5990b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // CharSequence overrides. 6000b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // 6010b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka // Just proxy for underlying mText. 6020b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 6030b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 6040b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public int length() { 6050b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.length(); 6060b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 6070b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 6080b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 6090b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public char charAt(int index) { 6100b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.charAt(index); 6110b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 6120b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 6130b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 6140b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public CharSequence subSequence(int start, int end) { 6150b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.subSequence(start, end); 6160b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 6170b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka 6180b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka @Override 6190b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka public String toString() { 6200b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka return mText.toString(); 6210b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka } 6220b4a6a291d2589c64f1ef0682aa403bc409090b7Seigo Nonaka} 623