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