19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcel;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcelable;
22d8415f4bf061000b049143b4f48b96b2005450bbAmith Yamasaniimport android.os.SystemProperties;
23d8415f4bf061000b049143b4f48b96b2005450bbAmith Yamasaniimport android.provider.Settings;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.AbsoluteSizeSpan;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.AlignmentSpan;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.BackgroundColorSpan;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.BulletSpan;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.CharacterStyle;
29e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolinimport android.text.style.EasyEditSpan;
300eea6681519277310e1733d791bfc0342b8e5ceaGilles Debunneimport android.text.style.ForegroundColorSpan;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LeadingMarginSpan;
32df8ef4b139a8918895f8a5c62536123da06e81feVictoria Leaseimport android.text.style.LocaleSpan;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.MetricAffectingSpan;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.QuoteSpan;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.RelativeSizeSpan;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ReplacementSpan;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ScaleXSpan;
3828294cc74491885c7fe4442353026f01f5b773e8Gilles Debunneimport android.text.style.SpellCheckSpan;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StrikethroughSpan;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StyleSpan;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SubscriptSpan;
4228294cc74491885c7fe4442353026f01f5b773e8Gilles Debunneimport android.text.style.SuggestionRangeSpan;
43a00972ab9a95f945342fdbf58956992e01310398Gilles Debunneimport android.text.style.SuggestionSpan;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SuperscriptSpan;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.TextAppearanceSpan;
464f4ead481270d48f0374c40e72a77619a4ac2873Niels Egbertsimport android.text.style.TtsSpan;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.TypefaceSpan;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.URLSpan;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.UnderlineSpan;
50577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Leaseimport android.util.Log;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Printer;
52d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglioimport android.view.View;
538d2aa199774f50256617351bf9caaaef59d47c59Raph Levien
54cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltimport com.android.internal.R;
55cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltimport com.android.internal.util.ArrayUtils;
568d2aa199774f50256617351bf9caaaef59d47c59Raph Levien
57d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglioimport libcore.icu.ICU;
58cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
591e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunneimport java.lang.reflect.Array;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Iterator;
61d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglioimport java.util.Locale;
62e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport java.util.regex.Pattern;
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TextUtils {
65577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease    private static final String TAG = "TextUtils";
66577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease
67d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller    /* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..."
68d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller    private static final String ELLIPSIS_STRING = new String(ELLIPSIS_NORMAL);
69d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller
70d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller    /* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".."
71d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller    private static final String ELLIPSIS_TWO_DOTS_STRING = new String(ELLIPSIS_TWO_DOTS);
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
73cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio    private TextUtils() { /* cannot be instantiated */ }
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void getChars(CharSequence s, int start, int end,
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                char[] dest, int destoff) {
771e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        Class<? extends CharSequence> c = s.getClass();
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c == String.class)
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((String) s).getChars(start, end, dest, destoff);
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (c == StringBuffer.class)
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((StringBuffer) s).getChars(start, end, dest, destoff);
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (c == StringBuilder.class)
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((StringBuilder) s).getChars(start, end, dest, destoff);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (s instanceof GetChars)
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((GetChars) s).getChars(start, end, dest, destoff);
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else {
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = start; i < end; i++)
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff++] = s.charAt(i);
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, char ch) {
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, ch, 0);
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, char ch, int start) {
981e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        Class<? extends CharSequence> c = s.getClass();
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c == String.class)
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((String) s).indexOf(ch, start);
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, ch, start, s.length());
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, char ch, int start, int end) {
1071e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        Class<? extends CharSequence> c = s.getClass();
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (s instanceof GetChars || c == StringBuffer.class ||
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c == StringBuilder.class || c == String.class) {
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int INDEX_INCREMENT = 500;
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] temp = obtain(INDEX_INCREMENT);
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (start < end) {
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int segend = start + INDEX_INCREMENT;
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (segend > end)
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    segend = end;
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                getChars(s, start, segend, temp, 0);
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int count = segend - start;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < count; i++) {
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (temp[i] == ch) {
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        recycle(temp);
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return i + start;
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                start = segend;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recycle(temp);
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = start; i < end; i++)
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (s.charAt(i) == ch)
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int lastIndexOf(CharSequence s, char ch) {
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return lastIndexOf(s, ch, s.length() - 1);
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int lastIndexOf(CharSequence s, char ch, int last) {
1481e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        Class<? extends CharSequence> c = s.getClass();
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c == String.class)
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((String) s).lastIndexOf(ch, last);
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return lastIndexOf(s, ch, 0, last);
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int lastIndexOf(CharSequence s, char ch,
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                  int start, int last) {
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (last < 0)
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (last >= s.length())
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            last = s.length() - 1;
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end = last + 1;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1651e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        Class<? extends CharSequence> c = s.getClass();
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (s instanceof GetChars || c == StringBuffer.class ||
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c == StringBuilder.class || c == String.class) {
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int INDEX_INCREMENT = 500;
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] temp = obtain(INDEX_INCREMENT);
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (start < end) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int segstart = end - INDEX_INCREMENT;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (segstart < start)
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    segstart = start;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                getChars(s, segstart, end, temp, 0);
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int count = end - segstart;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = count - 1; i >= 0; i--) {
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (temp[i] == ch) {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        recycle(temp);
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return i + segstart;
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end = segstart;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recycle(temp);
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = end - 1; i >= start; i--)
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (s.charAt(i) == ch)
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, CharSequence needle) {
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, needle, 0, s.length());
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, CharSequence needle, int start) {
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, needle, start, s.length());
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, CharSequence needle,
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              int start, int end) {
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int nlen = needle.length();
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (nlen == 0)
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return start;
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = needle.charAt(0);
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (;;) {
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start = indexOf(s, c, start);
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start > end - nlen) {
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start < 0) {
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return -1;
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (regionMatches(s, start, needle, 0, nlen)) {
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return start;
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start++;
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean regionMatches(CharSequence one, int toffset,
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        CharSequence two, int ooffset,
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        int len) {
2398d2aa199774f50256617351bf9caaaef59d47c59Raph Levien        int tempLen = 2 * len;
2408d2aa199774f50256617351bf9caaaef59d47c59Raph Levien        if (tempLen < len) {
2418d2aa199774f50256617351bf9caaaef59d47c59Raph Levien            // Integer overflow; len is unreasonably large
2428d2aa199774f50256617351bf9caaaef59d47c59Raph Levien            throw new IndexOutOfBoundsException();
2438d2aa199774f50256617351bf9caaaef59d47c59Raph Levien        }
2448d2aa199774f50256617351bf9caaaef59d47c59Raph Levien        char[] temp = obtain(tempLen);
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        getChars(one, toffset, toffset + len, temp, 0);
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        getChars(two, ooffset, ooffset + len, temp, len);
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean match = true;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i++) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (temp[i] != temp[i + len]) {
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                match = false;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recycle(temp);
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return match;
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create a new String object containing the given range of characters
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * from the source string.  This is different than simply calling
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link CharSequence#subSequence(int, int) CharSequence.subSequence}
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in that it does not preserve any style runs in the source sequence,
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * allowing a more efficient implementation.
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String substring(CharSequence source, int start, int end) {
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof String)
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((String) source).substring(start, end);
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof StringBuilder)
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((StringBuilder) source).substring(start, end);
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof StringBuffer)
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((StringBuffer) source).substring(start, end);
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] temp = obtain(end - start);
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        getChars(source, start, end, temp, 0);
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String ret = new String(temp, 0, end - start);
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recycle(temp);
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
285fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey     * Returns list of multiple {@link CharSequence} joined into a single
286fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey     * {@link CharSequence} separated by localized delimiter such as ", ".
287fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey     *
288fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey     * @hide
289fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey     */
290fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey    public static CharSequence join(Iterable<CharSequence> list) {
291fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey        final CharSequence delimiter = Resources.getSystem().getText(R.string.list_delimeter);
292fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey        return join(delimiter, list);
293fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey    }
294fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey
295fa4d7754edcbca7a3f651fe319e42d7624518452Jeff Sharkey    /**
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a string containing the tokens joined by delimiters.
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tokens an array objects to be joined. Strings will be formed from
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     the objects by calling object.toString().
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String join(CharSequence delimiter, Object[] tokens) {
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean firstTime = true;
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Object token: tokens) {
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (firstTime) {
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                firstTime = false;
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(delimiter);
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(token);
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a string containing the tokens joined by delimiters.
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tokens an array objects to be joined. Strings will be formed from
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     the objects by calling object.toString().
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String join(CharSequence delimiter, Iterable tokens) {
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean firstTime = true;
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Object token: tokens) {
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (firstTime) {
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                firstTime = false;
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(delimiter);
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(token);
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * String.split() returns [''] when the string to be split is empty. This returns []. This does
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * not remove any empty strings from the result. For example split("a,", ","  ) returns {"a", ""}.
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text the string to split
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param expression the regular expression to match
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return an array of strings. The array will be empty if text is empty
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if expression or text is null
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String[] split(String text, String expression) {
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length() == 0) {
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return EMPTY_STRING_ARRAY;
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return text.split(expression, -1);
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Splits a string on a pattern. String.split() returns [''] when the string to be
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * split is empty. This returns []. This does not remove any empty strings from the result.
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text the string to split
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param pattern the regular expression to match
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return an array of strings. The array will be empty if text is empty
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if expression or text is null
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String[] split(String text, Pattern pattern) {
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length() == 0) {
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return EMPTY_STRING_ARRAY;
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return pattern.split(text, -1);
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * An interface for splitting strings according to rules that are opaque to the user of this
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * interface. This also has less overhead than split, which uses regular expressions and
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * allocates an array to hold the results.
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>The most efficient way to use this class is:
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <pre>
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * // Once
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(delimiter);
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * // Once per string to split
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * splitter.setString(string);
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * for (String s : splitter) {
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     ...
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * }
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * </pre>
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface StringSplitter extends Iterable<String> {
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setString(String string);
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * A simple string splitter.
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>If the final character in the string to split is the delimiter then no empty string will
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be returned for the empty string after that delimeter. That is, splitting <tt>"a,b,"</tt> on
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * comma will return <tt>"a", "b"</tt>, not <tt>"a", "b", ""</tt>.
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class SimpleStringSplitter implements StringSplitter, Iterator<String> {
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mString;
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private char mDelimiter;
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mPosition;
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mLength;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Initializes the splitter. setString may be called later.
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param delimiter the delimeter on which to split
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public SimpleStringSplitter(char delimiter) {
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDelimiter = delimiter;
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the string to split
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param string the string to split
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setString(String string) {
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mString = string;
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPosition = 0;
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLength = mString.length();
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Iterator<String> iterator() {
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean hasNext() {
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mPosition < mLength;
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String next() {
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = mString.indexOf(mDelimiter, mPosition);
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end == -1) {
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end = mLength;
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String nextString = mString.substring(mPosition, end);
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPosition = end + 1; // Skip the delimiter.
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return nextString;
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void remove() {
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new UnsupportedOperationException();
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence stringOrSpannedString(CharSequence source) {
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source == null)
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof SpannedString)
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return source;
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof Spanned)
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new SpannedString(source);
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return source.toString();
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if the string is null or 0-length.
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param str the string to be examined
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if str is null or zero length
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isEmpty(CharSequence str) {
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (str == null || str.length() == 0)
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the length that the specified CharSequence would have if
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * spaces and control characters were trimmed from the start and end,
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * as by {@link String#trim}.
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getTrimmedLength(CharSequence s) {
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = s.length();
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = 0;
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (start < len && s.charAt(start) <= ' ') {
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start++;
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end = len;
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (end > start && s.charAt(end - 1) <= ' ') {
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end--;
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return end - start;
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if a and b are equal, including if they are both null.
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p><i>Note: In platform versions 1.1 and earlier, this method only worked well if
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * both the arguments were instances of String.</i></p>
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param a first CharSequence to check
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param b second CharSequence to check
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if a and b are equal
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean equals(CharSequence a, CharSequence b) {
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (a == b) return true;
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int length;
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (a != null && b != null && (length = a.length()) == b.length()) {
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (a instanceof String && b instanceof String) {
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return a.equals(b);
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < length; i++) {
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (a.charAt(i) != b.charAt(i)) return false;
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // XXX currently this only reverses chars, not spans
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence getReverse(CharSequence source,
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                          int start, int end) {
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new Reverser(source, start, end);
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Reverser
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    implements CharSequence, GetChars
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Reverser(CharSequence source, int start, int end) {
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSource = source;
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStart = start;
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEnd = end;
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int length() {
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mEnd - mStart;
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence subSequence(int start, int end) {
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] buf = new char[end - start];
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(start, end, buf, 0);
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new String(buf);
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5381e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        @Override
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return subSequence(0, length()).toString();
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public char charAt(int off) {
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return AndroidCharacter.getMirror(mSource.charAt(mEnd - 1 - off));
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void getChars(int start, int end, char[] dest, int destoff) {
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.getChars(mSource, start + mStart, end + mStart,
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               dest, destoff);
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            AndroidCharacter.mirror(dest, 0, end - start);
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int len = end - start;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int n = (end - start) / 2;
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < n; i++) {
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char tmp = dest[destoff + i];
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff + i] = dest[destoff + len - i - 1];
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff + len - i - 1] = tmp;
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private CharSequence mSource;
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mStart;
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mEnd;
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int ALIGNMENT_SPAN = 1;
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
570577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease    public static final int FIRST_SPAN = ALIGNMENT_SPAN;
571577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease    /** @hide */
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int FOREGROUND_COLOR_SPAN = 2;
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int RELATIVE_SIZE_SPAN = 3;
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int SCALE_X_SPAN = 4;
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int STRIKETHROUGH_SPAN = 5;
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int UNDERLINE_SPAN = 6;
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int STYLE_SPAN = 7;
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int BULLET_SPAN = 8;
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int QUOTE_SPAN = 9;
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int LEADING_MARGIN_SPAN = 10;
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int URL_SPAN = 11;
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int BACKGROUND_COLOR_SPAN = 12;
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int TYPEFACE_SPAN = 13;
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int SUPERSCRIPT_SPAN = 14;
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int SUBSCRIPT_SPAN = 15;
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int ABSOLUTE_SIZE_SPAN = 16;
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int TEXT_APPEARANCE_SPAN = 17;
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int ANNOTATION = 18;
605adb435835fb9a5f2bb74d29930b239dde18504a7satok    /** @hide */
606a00972ab9a95f945342fdbf58956992e01310398Gilles Debunne    public static final int SUGGESTION_SPAN = 19;
60728294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne    /** @hide */
60828294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne    public static final int SPELL_CHECK_SPAN = 20;
60928294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne    /** @hide */
61028294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne    public static final int SUGGESTION_RANGE_SPAN = 21;
611e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin    /** @hide */
612e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin    public static final int EASY_EDIT_SPAN = 22;
613df8ef4b139a8918895f8a5c62536123da06e81feVictoria Lease    /** @hide */
614df8ef4b139a8918895f8a5c62536123da06e81feVictoria Lease    public static final int LOCALE_SPAN = 23;
615577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease    /** @hide */
6164f4ead481270d48f0374c40e72a77619a4ac2873Niels Egberts    public static final int TTS_SPAN = 24;
6174f4ead481270d48f0374c40e72a77619a4ac2873Niels Egberts    /** @hide */
6184f4ead481270d48f0374c40e72a77619a4ac2873Niels Egberts    public static final int LAST_SPAN = TTS_SPAN;
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Flatten a CharSequence and whatever styles can be copied across processes
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * into the parcel.
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void writeToParcel(CharSequence cs, Parcel p,
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int parcelableFlags) {
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cs instanceof Spanned) {
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeInt(0);
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeString(cs.toString());
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Spanned sp = (Spanned) cs;
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Object[] os = sp.getSpans(0, cs.length(), Object.class);
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // note to people adding to this: check more specific types
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // before more generic types.  also notice that it uses
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // "if" instead of "else if" where there are interfaces
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // so one object can be several.
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < os.length; i++) {
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object o = os[i];
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object prop = os[i];
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (prop instanceof CharacterStyle) {
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    prop = ((CharacterStyle) prop).getUnderlying();
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (prop instanceof ParcelableSpan) {
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ParcelableSpan ps = (ParcelableSpan)prop;
648577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                    int spanTypeId = ps.getSpanTypeId();
649577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                    if (spanTypeId < FIRST_SPAN || spanTypeId > LAST_SPAN) {
650577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                        Log.e(TAG, "external class \"" + ps.getClass().getSimpleName()
651577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                                + "\" is attempting to use the frameworks-only ParcelableSpan"
652577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                                + " interface");
653577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                    } else {
654577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                        p.writeInt(spanTypeId);
655577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                        ps.writeToParcel(p, parcelableFlags);
656577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                        writeWhere(p, sp, o);
657577ba5354bf5a2899021e385ac9ca3cc07d35a60Victoria Lease                    }
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeInt(0);
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeInt(1);
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cs != null) {
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                p.writeString(cs.toString());
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                p.writeString(null);
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void writeWhere(Parcel p, Spanned sp, Object o) {
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        p.writeInt(sp.getSpanStart(o));
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        p.writeInt(sp.getSpanEnd(o));
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        p.writeInt(sp.getSpanFlags(o));
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final Parcelable.Creator<CharSequence> CHAR_SEQUENCE_CREATOR
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = new Parcelable.Creator<CharSequence>() {
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Read and return a new CharSequence, possibly with styles,
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * from the parcel.
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
6841e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        public CharSequence createFromParcel(Parcel p) {
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int kind = p.readInt();
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
687cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren            String string = p.readString();
688cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren            if (string == null) {
689cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren                return null;
690cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren            }
691cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren
692cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren            if (kind == 1) {
693cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren                return string;
694cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren            }
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
696cee2051adac53a85653ba8ead3a671c0978af43bMartin Wallgren            SpannableString sp = new SpannableString(string);
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (true) {
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                kind = p.readInt();
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (kind == 0)
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switch (kind) {
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case ALIGNMENT_SPAN:
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new AlignmentSpan.Standard(p));
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case FOREGROUND_COLOR_SPAN:
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new ForegroundColorSpan(p));
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case RELATIVE_SIZE_SPAN:
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new RelativeSizeSpan(p));
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case SCALE_X_SPAN:
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new ScaleXSpan(p));
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case STRIKETHROUGH_SPAN:
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new StrikethroughSpan(p));
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case UNDERLINE_SPAN:
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new UnderlineSpan(p));
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case STYLE_SPAN:
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new StyleSpan(p));
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case BULLET_SPAN:
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new BulletSpan(p));
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case QUOTE_SPAN:
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new QuoteSpan(p));
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case LEADING_MARGIN_SPAN:
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new LeadingMarginSpan.Standard(p));
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case URL_SPAN:
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new URLSpan(p));
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case BACKGROUND_COLOR_SPAN:
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new BackgroundColorSpan(p));
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TYPEFACE_SPAN:
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new TypefaceSpan(p));
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case SUPERSCRIPT_SPAN:
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new SuperscriptSpan(p));
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case SUBSCRIPT_SPAN:
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new SubscriptSpan(p));
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case ABSOLUTE_SIZE_SPAN:
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new AbsoluteSizeSpan(p));
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TEXT_APPEARANCE_SPAN:
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new TextAppearanceSpan(p));
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case ANNOTATION:
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new Annotation(p));
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
777a00972ab9a95f945342fdbf58956992e01310398Gilles Debunne                case SUGGESTION_SPAN:
778a00972ab9a95f945342fdbf58956992e01310398Gilles Debunne                    readSpan(p, sp, new SuggestionSpan(p));
779a00972ab9a95f945342fdbf58956992e01310398Gilles Debunne                    break;
780a00972ab9a95f945342fdbf58956992e01310398Gilles Debunne
78128294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne                case SPELL_CHECK_SPAN:
78228294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne                    readSpan(p, sp, new SpellCheckSpan(p));
78328294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne                    break;
78428294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne
78528294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne                case SUGGESTION_RANGE_SPAN:
7860eea6681519277310e1733d791bfc0342b8e5ceaGilles Debunne                    readSpan(p, sp, new SuggestionRangeSpan(p));
78728294cc74491885c7fe4442353026f01f5b773e8Gilles Debunne                    break;
788e90bed18cc123c0963bbcc023976fa355c16a352Gilles Debunne
789e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                case EASY_EDIT_SPAN:
7901b15ba5d194c1db71d0a34ee110bd1ab169c8a29Luca Zanolin                    readSpan(p, sp, new EasyEditSpan(p));
791e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                    break;
792e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin
793df8ef4b139a8918895f8a5c62536123da06e81feVictoria Lease                case LOCALE_SPAN:
794df8ef4b139a8918895f8a5c62536123da06e81feVictoria Lease                    readSpan(p, sp, new LocaleSpan(p));
795df8ef4b139a8918895f8a5c62536123da06e81feVictoria Lease                    break;
796df8ef4b139a8918895f8a5c62536123da06e81feVictoria Lease
7974f4ead481270d48f0374c40e72a77619a4ac2873Niels Egberts                case TTS_SPAN:
7984f4ead481270d48f0374c40e72a77619a4ac2873Niels Egberts                    readSpan(p, sp, new TtsSpan(p));
7994f4ead481270d48f0374c40e72a77619a4ac2873Niels Egberts                    break;
8004f4ead481270d48f0374c40e72a77619a4ac2873Niels Egberts
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                default:
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    throw new RuntimeException("bogus span encoding " + kind);
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sp;
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence[] newArray(int size)
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new CharSequence[size];
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Debugging tool to print the spans in a CharSequence.  The output will
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be printed one span per line.  If the CharSequence is not a Spanned,
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * then the entire string will be printed on a single line.
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void dumpSpans(CharSequence cs, Printer printer, String prefix) {
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cs instanceof Spanned) {
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Spanned sp = (Spanned) cs;
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Object[] os = sp.getSpans(0, cs.length(), Object.class);
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < os.length; i++) {
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object o = os[i];
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                printer.println(prefix + cs.subSequence(sp.getSpanStart(o),
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sp.getSpanEnd(o)) + ": "
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        + Integer.toHexString(System.identityHashCode(o))
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        + " " + o.getClass().getCanonicalName()
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         + " (" + sp.getSpanStart(o) + "-" + sp.getSpanEnd(o)
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         + ") fl=#" + sp.getSpanFlags(o));
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            printer.println(prefix + cs + ": (no spans)");
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return a new CharSequence in which each of the source strings is
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * replaced by the corresponding element of the destinations.
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence replace(CharSequence template,
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       String[] sources,
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       CharSequence[] destinations) {
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpannableStringBuilder tb = new SpannableStringBuilder(template);
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < sources.length; i++) {
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int where = indexOf(tb, sources[i]);
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (where >= 0)
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                tb.setSpan(sources[i], where, where + sources[i].length(),
8531e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                           Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < sources.length; i++) {
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start = tb.getSpanStart(sources[i]);
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = tb.getSpanEnd(sources[i]);
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start >= 0) {
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                tb.replace(start, end, destinations[i]);
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return tb;
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Replace instances of "^1", "^2", etc. in the
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>template</code> CharSequence with the corresponding
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>values</code>.  "^^" is used to produce a single caret in
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the output.  Only up to 9 replacement values are supported,
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * "^10" will be produce the first replacement value followed by a
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * '0'.
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param template the input text containing "^1"-style
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * placeholder values.  This object is not modified; a copy is
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * returned.
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param values CharSequences substituted into the template.  The
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * first is substituted for "^1", the second for "^2", and so on.
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the new CharSequence produced by doing the replacement
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws IllegalArgumentException if the template requests a
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * value that was not provided, or if more than 9 values are
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * provided.
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence expandTemplate(CharSequence template,
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              CharSequence... values) {
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (values.length > 9) {
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("max of 9 values are supported");
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpannableStringBuilder ssb = new SpannableStringBuilder(template);
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = 0;
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (i < ssb.length()) {
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ssb.charAt(i) == '^') {
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char next = ssb.charAt(i+1);
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (next == '^') {
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ssb.delete(i+1, i+2);
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ++i;
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (Character.isDigit(next)) {
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int which = Character.getNumericValue(next) - 1;
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (which < 0) {
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            throw new IllegalArgumentException(
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "template requests value ^" + (which+1));
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (which >= values.length) {
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            throw new IllegalArgumentException(
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "template requests value ^" + (which+1) +
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "; only " + values.length + " provided");
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ssb.replace(i, i+2, values[which]);
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += values[which].length();
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ++i;
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IndexOutOfBoundsException ignore) {
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // happens when ^ is the last character in the string.
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ssb;
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getOffsetBefore(CharSequence text, int offset) {
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == 0)
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == 1)
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = text.charAt(offset - 1);
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c >= '\uDC00' && c <= '\uDFFF') {
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c1 = text.charAt(offset - 2);
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c1 >= '\uD800' && c1 <= '\uDBFF')
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset -= 2;
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset -= 1;
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            offset -= 1;
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text instanceof Spanned) {
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset,
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                       ReplacementSpan.class);
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < spans.length; i++) {
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = ((Spanned) text).getSpanStart(spans[i]);
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = ((Spanned) text).getSpanEnd(spans[i]);
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start < offset && end > offset)
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    offset = start;
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return offset;
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getOffsetAfter(CharSequence text, int offset) {
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == len)
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return len;
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == len - 1)
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return len;
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = text.charAt(offset);
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c >= '\uD800' && c <= '\uDBFF') {
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c1 = text.charAt(offset + 1);
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c1 >= '\uDC00' && c1 <= '\uDFFF')
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset += 2;
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset += 1;
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            offset += 1;
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text instanceof Spanned) {
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset,
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                       ReplacementSpan.class);
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < spans.length; i++) {
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = ((Spanned) text).getSpanStart(spans[i]);
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = ((Spanned) text).getSpanEnd(spans[i]);
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start < offset && end > offset)
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    offset = end;
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return offset;
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void readSpan(Parcel p, Spannable sp, Object o) {
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sp.setSpan(o, p.readInt(), p.readInt(), p.readInt());
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1006c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa    /**
1007c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * Copies the spans from the region <code>start...end</code> in
1008c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * <code>source</code> to the region
1009c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * <code>destoff...destoff+end-start</code> in <code>dest</code>.
1010c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * Spans in <code>source</code> that begin before <code>start</code>
1011c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * or end after <code>end</code> but overlap this range are trimmed
1012c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * as if they began at <code>start</code> or ended at <code>end</code>.
1013c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     *
1014c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * @throws IndexOutOfBoundsException if any of the copied spans
1015c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * are out of range in <code>dest</code>.
1016c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     */
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void copySpansFrom(Spanned source, int start, int end,
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                     Class kind,
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                     Spannable dest, int destoff) {
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (kind == null) {
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            kind = Object.class;
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = source.getSpans(start, end, kind);
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < spans.length; i++) {
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int st = source.getSpanStart(spans[i]);
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int en = source.getSpanEnd(spans[i]);
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int fl = source.getSpanFlags(spans[i]);
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (st < start)
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                st = start;
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (en > end)
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                en = end;
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.setSpan(spans[i], st - start + destoff, en - start + destoff,
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         fl);
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum TruncateAt {
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        START,
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MIDDLE,
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        END,
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MARQUEE,
1046cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        /**
1047cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio         * @hide
1048cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio         */
1049cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        END_SMALL
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface EllipsizeCallback {
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This method is called to report that the specified region of
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * text was ellipsized away by a call to {@link #ellipsize}.
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void ellipsized(int start, int end);
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the original text if it fits in the specified width
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * given the properties of the specified Paint,
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * or, if it does not fit, a truncated
10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * copy with ellipsis character added at the specified edge or center.
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence ellipsize(CharSequence text,
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         TextPaint p,
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         float avail, TruncateAt where) {
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ellipsize(text, p, avail, where, false, null);
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the original text if it fits in the specified width
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * given the properties of the specified Paint,
1075e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * or, if it does not fit, a copy with ellipsis character added
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * at the specified edge or center.
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If <code>preserveLength</code> is specified, the returned copy
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * will be padded with zero-width spaces to preserve the original
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * length and offsets instead of truncating.
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If <code>callback</code> is non-null, it will be called to
1081cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * report the start and end of the ellipsized range.  TextDirection
1082cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * is determined by the first strong directional character.
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence ellipsize(CharSequence text,
1085e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                         TextPaint paint,
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         float avail, TruncateAt where,
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         boolean preserveLength,
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         EllipsizeCallback callback) {
1089cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        return ellipsize(text, paint, avail, where, preserveLength, callback,
1090cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                TextDirectionHeuristics.FIRSTSTRONG_LTR,
1091d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller                (where == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS_STRING : ELLIPSIS_STRING);
1092cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
1093cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
1094cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
1095cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * Returns the original text if it fits in the specified width
1096cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * given the properties of the specified Paint,
1097cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * or, if it does not fit, a copy with ellipsis character added
1098cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * at the specified edge or center.
1099cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * If <code>preserveLength</code> is specified, the returned copy
1100cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * will be padded with zero-width spaces to preserve the original
1101cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * length and offsets instead of truncating.
1102cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * If <code>callback</code> is non-null, it will be called to
1103cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * report the start and end of the ellipsized range.
1104cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     *
1105cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
1106cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
1107cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public static CharSequence ellipsize(CharSequence text,
1108cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint,
1109cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float avail, TruncateAt where,
1110cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean preserveLength,
1111cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            EllipsizeCallback callback,
1112cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio            TextDirectionHeuristic textDir, String ellipsis) {
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MeasuredText mt = MeasuredText.obtain();
1117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        try {
1118cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float width = setPara(mt, paint, text, 0, text.length(), textDir);
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (width <= avail) {
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (callback != null) {
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    callback.ellipsized(0, 0);
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return text;
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // XXX assumes ellipsis string does not require shaping and
1129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // is unaffected by style
1130cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio            float ellipsiswid = paint.measureText(ellipsis);
1131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            avail -= ellipsiswid;
1132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int left = 0;
1134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int right = len;
1135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (avail < 0) {
1136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                // it all goes
1137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else if (where == TruncateAt.START) {
1138c70e7a0b8add16d2e6cec4d58c3cc74d08cc20b4Gilles Debunne                right = len - mt.breakText(len, false, avail);
1139cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio            } else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
1140c70e7a0b8add16d2e6cec4d58c3cc74d08cc20b4Gilles Debunne                left = mt.breakText(len, true, avail);
1141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
1142c70e7a0b8add16d2e6cec4d58c3cc74d08cc20b4Gilles Debunne                right = len - mt.breakText(len, false, avail / 2);
1143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                avail -= mt.measure(right, len);
1144c70e7a0b8add16d2e6cec4d58c3cc74d08cc20b4Gilles Debunne                left = mt.breakText(right, true, avail);
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (callback != null) {
1148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                callback.ellipsized(left, right);
11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1151e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            char[] buf = mt.mChars;
1152e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            Spanned sp = text instanceof Spanned ? (Spanned) text : null;
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1154e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int remaining = len - (right - left);
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (preserveLength) {
1156e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (remaining > 0) { // else eliminate the ellipsis too
1157cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                    buf[left++] = ellipsis.charAt(0);
1158e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
1159e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                for (int i = left; i < right; i++) {
1160cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                    buf[i] = ZWNBS_CHAR;
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1162e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                String s = new String(buf, 0, len);
1163e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (sp == null) {
1164e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return s;
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1166e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                SpannableString ss = new SpannableString(s);
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                copySpansFrom(sp, 0, len, Object.class, ss, 0);
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return ss;
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (remaining == 0) {
1172e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return "";
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1175e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (sp == null) {
1176cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                StringBuilder sb = new StringBuilder(remaining + ellipsis.length());
1177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                sb.append(buf, 0, left);
1178cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                sb.append(ellipsis);
1179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                sb.append(buf, right, len - right);
1180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return sb.toString();
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            SpannableStringBuilder ssb = new SpannableStringBuilder();
1184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            ssb.append(text, 0, left);
1185cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio            ssb.append(ellipsis);
1186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            ssb.append(text, right, len);
1187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return ssb;
1188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } finally {
1189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            MeasuredText.recycle(mt);
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Converts a CharSequence of the comma-separated form "Andy, Bob,
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Charles, David" that is too wide to fit into the specified width
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * into one like "Andy, Bob, 2 more".
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text the text to truncate
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param p the Paint with which to measure the text
12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param avail the horizontal width available for the text
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param oneMore the string for "1 more" in the current locale
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param more the string for "%d more" in the current locale
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence commaEllipsize(CharSequence text,
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              TextPaint p, float avail,
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              String oneMore,
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              String more) {
1208cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        return commaEllipsize(text, p, avail, oneMore, more,
1209cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                TextDirectionHeuristics.FIRSTSTRONG_LTR);
1210cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
1211cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
1212cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
1213cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
1214cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
1215cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
1216cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt         float avail, String oneMore, String more, TextDirectionHeuristic textDir) {
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MeasuredText mt = MeasuredText.obtain();
1219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        try {
1220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int len = text.length();
1221cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float width = setPara(mt, p, text, 0, len, textDir);
1222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (width <= avail) {
1223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return text;
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            char[] buf = mt.mChars;
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int commaCount = 0;
1229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int i = 0; i < len; i++) {
1230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (buf[i] == ',') {
1231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    commaCount++;
1232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int remaining = commaCount + 1;
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int ok = 0;
1238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            String okFormat = "";
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int w = 0;
1241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int count = 0;
1242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float[] widths = mt.mWidths;
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            MeasuredText tempMt = MeasuredText.obtain();
1245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int i = 0; i < len; i++) {
1246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                w += widths[i];
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (buf[i] == ',') {
1249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    count++;
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    String format;
1252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // XXX should not insert spaces, should be part of string
1253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // XXX should use plural rules and not assume English plurals
1254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (--remaining == 1) {
1255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        format = " " + oneMore;
1256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    } else {
1257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        format = " " + String.format(more, remaining);
1258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // XXX this is probably ok, but need to look at it more
1261cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                    tempMt.setPara(format, 0, format.length(), textDir);
12624c8ad6eb6241a0f689e49237ecadb65e8ffa4b6cBrian Muramatsu                    float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null);
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (w + moreWid <= avail) {
1265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        ok = i + 1;
1266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        okFormat = format;
1267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            MeasuredText.recycle(tempMt);
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SpannableStringBuilder out = new SpannableStringBuilder(okFormat);
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.insert(0, text, 0, ok);
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return out;
1275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } finally {
1276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            MeasuredText.recycle(mt);
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static float setPara(MeasuredText mt, TextPaint paint,
1281cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
1282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1283cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        mt.setPara(text, start, end, textDir);
1284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float width;
1286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        Spanned sp = text instanceof Spanned ? (Spanned) text : null;
1287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int len = end - start;
1288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (sp == null) {
1289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            width = mt.addStyleRun(paint, len, null);
1290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
1291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            width = 0;
1292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int spanEnd;
1293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int spanStart = 0; spanStart < len; spanStart = spanEnd) {
1294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                spanEnd = sp.nextSpanTransition(spanStart, len,
1295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan.class);
1296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan[] spans = sp.getSpans(
1297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        spanStart, spanEnd, MetricAffectingSpan.class);
12981e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                spans = TextUtils.removeEmptySpans(spans, sp, MetricAffectingSpan.class);
1299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                width += mt.addStyleRun(paint, spans, spanEnd - spanStart, null);
1300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
1301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return width;
1304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final char FIRST_RIGHT_TO_LEFT = '\u0590';
1307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /* package */
1309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static boolean doesNotNeedBidi(CharSequence s, int start, int end) {
1310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = start; i < end; i++) {
1311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (s.charAt(i) >= FIRST_RIGHT_TO_LEFT) {
1312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return false;
1313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
1314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return true;
1316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /* package */
1319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static boolean doesNotNeedBidi(char[] text, int start, int len) {
1320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = start, e = i + len; i < e; i++) {
1321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (text[i] >= FIRST_RIGHT_TO_LEFT) {
1322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return false;
1323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
1324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return true;
1326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static char[] obtain(int len) {
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] buf;
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (sLock) {
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            buf = sTemp;
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sTemp = null;
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (buf == null || buf.length < len)
1337776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            buf = ArrayUtils.newUnpaddedCharArray(len);
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return buf;
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static void recycle(char[] temp) {
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (temp.length > 1000)
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (sLock) {
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sTemp = temp;
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Html-encode the string.
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param s the string to be encoded
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the encoded string
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String htmlEncode(String s) {
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c;
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < s.length(); i++) {
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = s.charAt(i);
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (c) {
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '<':
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&lt;"); //$NON-NLS-1$
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '>':
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&gt;"); //$NON-NLS-1$
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '&':
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&amp;"); //$NON-NLS-1$
13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '\'':
1372f4832da16a9513e19881397abdbbf62285f8b852Marc Blank                //http://www.w3.org/TR/xhtml1
1373f4832da16a9513e19881397abdbbf62285f8b852Marc Blank                // The named character reference &apos; (the apostrophe, U+0027) was introduced in
1374f4832da16a9513e19881397abdbbf62285f8b852Marc Blank                // XML 1.0 but does not appear in HTML. Authors should therefore use &#39; instead
1375f4832da16a9513e19881397abdbbf62285f8b852Marc Blank                // of &apos; to work as expected in HTML 4 user agents.
1376f4832da16a9513e19881397abdbbf62285f8b852Marc Blank                sb.append("&#39;"); //$NON-NLS-1$
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '"':
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&quot;"); //$NON-NLS-1$
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            default:
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(c);
13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a CharSequence concatenating the specified CharSequences,
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * retaining their spans if any.
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence concat(CharSequence... text) {
13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length == 0) {
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "";
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length == 1) {
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return text[0];
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean spanned = false;
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < text.length; i++) {
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (text[i] instanceof Spanned) {
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                spanned = true;
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < text.length; i++) {
14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(text[i]);
14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!spanned) {
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sb.toString();
14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpannableString ss = new SpannableString(sb);
14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = 0;
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < text.length; i++) {
14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int len = text[i].length();
14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (text[i] instanceof Spanned) {
14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                copySpansFrom((Spanned) text[i], 0, len, Object.class, ss, off);
14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            off += len;
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new SpannedString(ss);
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether the given CharSequence contains any printable characters.
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isGraphic(CharSequence str) {
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int len = str.length();
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i=0; i<len; i++) {
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int gc = Character.getType(str.charAt(i));
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (gc != Character.CONTROL
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.FORMAT
14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.SURROGATE
14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.UNASSIGNED
14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.LINE_SEPARATOR
14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.PARAGRAPH_SEPARATOR
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.SPACE_SEPARATOR) {
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether this character is a printable character.
14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isGraphic(char c) {
14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int gc = Character.getType(c);
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return     gc != Character.CONTROL
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.FORMAT
14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.SURROGATE
14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.UNASSIGNED
14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.LINE_SEPARATOR
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.PARAGRAPH_SEPARATOR
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.SPACE_SEPARATOR;
14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether the given CharSequence contains only digits.
14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isDigitsOnly(CharSequence str) {
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int len = str.length();
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i++) {
14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!Character.isDigit(str.charAt(i))) {
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1481973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     * @hide
1482973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     */
1483973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    public static boolean isPrintableAscii(final char c) {
1484973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        final int asciiFirst = 0x20;
1485973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        final int asciiLast = 0x7E;  // included
1486973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        return (asciiFirst <= c && c <= asciiLast) || c == '\r' || c == '\n';
1487973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    }
1488973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa
1489973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    /**
1490973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     * @hide
1491973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     */
1492973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    public static boolean isPrintableAsciiOnly(final CharSequence str) {
1493973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        final int len = str.length();
1494973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        for (int i = 0; i < len; i++) {
1495973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa            if (!isPrintableAscii(str.charAt(i))) {
1496973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa                return false;
1497973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa            }
1498973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        }
1499973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        return true;
1500973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    }
1501973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa
1502973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    /**
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Capitalization mode for {@link #getCapsMode}: capitalize all
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * characters.  This value is explicitly defined to be the same as
15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link InputType#TYPE_TEXT_FLAG_CAP_CHARACTERS}.
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int CAP_MODE_CHARACTERS
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
1509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Capitalization mode for {@link #getCapsMode}: capitalize the first
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * character of all words.  This value is explicitly defined to be the same as
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link InputType#TYPE_TEXT_FLAG_CAP_WORDS}.
15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int CAP_MODE_WORDS
15169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = InputType.TYPE_TEXT_FLAG_CAP_WORDS;
1517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
15189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Capitalization mode for {@link #getCapsMode}: capitalize the first
15209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * character of each sentence.  This value is explicitly defined to be the same as
15219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link InputType#TYPE_TEXT_FLAG_CAP_SENTENCES}.
15229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int CAP_MODE_SENTENCES
15249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
1525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
15269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Determine what caps mode should be in effect at the current offset in
15289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the text.  Only the mode bits set in <var>reqModes</var> will be
15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * checked.  Note that the caps mode flags here are explicitly defined
15309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to match those in {@link InputType}.
1531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
15329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param cs The text that should be checked for caps modes.
15339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param off Location in the text at which to check.
15349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param reqModes The modes to be checked: may be any combination of
15359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
15369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_SENTENCES}.
153760919953ce80dbf75673837ea51497c84da7ac78Mark Wagner     *
15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Returns the actual capitalization modes that can be in effect
15399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * at the current position, which is any combination of
15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
15419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_SENTENCES}.
15429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getCapsMode(CharSequence cs, int off, int reqModes) {
154460919953ce80dbf75673837ea51497c84da7ac78Mark Wagner        if (off < 0) {
154560919953ce80dbf75673837ea51497c84da7ac78Mark Wagner            return 0;
154660919953ce80dbf75673837ea51497c84da7ac78Mark Wagner        }
154760919953ce80dbf75673837ea51497c84da7ac78Mark Wagner
15489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i;
15499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c;
15509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int mode = 0;
15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((reqModes&CAP_MODE_CHARACTERS) != 0) {
15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mode |= CAP_MODE_CHARACTERS;
15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((reqModes&(CAP_MODE_WORDS|CAP_MODE_SENTENCES)) == 0) {
15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode;
15579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Back over allowed opening punctuation.
15609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (i = off; i > 0; i--) {
15629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = cs.charAt(i - 1);
15639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != '"' && c != '\'' &&
15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Character.getType(c) != Character.START_PUNCTUATION) {
15669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
15679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Start of paragraph, with optional whitespace.
15719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int j = i;
15739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (j > 0 && ((c = cs.charAt(j - 1)) == ' ' || c == '\t')) {
15749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            j--;
15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (j == 0 || cs.charAt(j - 1) == '\n') {
15779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode | CAP_MODE_WORDS;
15789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Or start of word if we are that style.
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((reqModes&CAP_MODE_SENTENCES) == 0) {
15839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i != j) mode |= CAP_MODE_WORDS;
15849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode;
15859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // There must be a space if not the start of paragraph.
15889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i == j) {
15909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode;
15919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Back over allowed closing punctuation.
15949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (; j > 0; j--) {
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = cs.charAt(j - 1);
15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != '"' && c != '\'' &&
15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Character.getType(c) != Character.END_PUNCTUATION) {
16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (j > 0) {
16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = cs.charAt(j - 1);
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '.' || c == '?' || c == '!') {
16089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Do not capitalize if the word ends with a period but
16099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // also contains a period, in which case it is an abbreviation.
16109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (c == '.') {
16129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int k = j - 2; k >= 0; k--) {
16139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        c = cs.charAt(k);
16149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (c == '.') {
16169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            return mode;
16179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
16189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (!Character.isLetter(c)) {
16209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
16219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
16229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
16239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mode | CAP_MODE_SENTENCES;
16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mode;
16309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1631e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
163211fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick    /**
163311fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick     * Does a comma-delimited list 'delimitedString' contain a certain item?
163411fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick     * (without allocating memory)
163511fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick     *
163611fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick     * @hide
163711fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick     */
163811fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick    public static boolean delimitedStringContains(
163911fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            String delimitedString, char delimiter, String item) {
164011fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick        if (isEmpty(delimitedString) || isEmpty(item)) {
164111fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            return false;
164211fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick        }
164311fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick        int pos = -1;
164411fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick        int length = delimitedString.length();
164511fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick        while ((pos = delimitedString.indexOf(item, pos + 1)) != -1) {
164611fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            if (pos > 0 && delimitedString.charAt(pos - 1) != delimiter) {
164711fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick                continue;
164811fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            }
164911fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            int expectedDelimiterPos = pos + item.length();
165011fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            if (expectedDelimiterPos == length) {
165111fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick                // Match at end of string.
165211fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick                return true;
165311fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            }
165411fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            if (delimitedString.charAt(expectedDelimiterPos) == delimiter) {
165511fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick                return true;
165611fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick            }
165711fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick        }
165811fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick        return false;
165911fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick    }
166011fe181e16501103d7c0f70344661ea2ef5d3df9Brad Fitzpatrick
16611e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne    /**
16621e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * Removes empty spans from the <code>spans</code> array.
16631e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     *
16641e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * When parsing a Spanned using {@link Spanned#nextSpanTransition(int, int, Class)}, empty spans
16651e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * will (correctly) create span transitions, and calling getSpans on a slice of text bounded by
16661e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * one of these transitions will (correctly) include the empty overlapping span.
16671e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     *
16681e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * However, these empty spans should not be taken into account when layouting or rendering the
16691e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * string and this method provides a way to filter getSpans' results accordingly.
16701e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     *
16711e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * @param spans A list of spans retrieved using {@link Spanned#getSpans(int, int, Class)} from
16721e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * the <code>spanned</code>
16731e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * @param spanned The Spanned from which spans were extracted
16741e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * @return A subset of spans where empty spans ({@link Spanned#getSpanStart(Object)}  ==
16751e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * {@link Spanned#getSpanEnd(Object)} have been removed. The initial order is preserved
16761e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     * @hide
16771e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne     */
16781e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne    @SuppressWarnings("unchecked")
16791e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne    public static <T> T[] removeEmptySpans(T[] spans, Spanned spanned, Class<T> klass) {
16801e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        T[] copy = null;
16811e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        int count = 0;
16821e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne
16831e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        for (int i = 0; i < spans.length; i++) {
16841e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            final T span = spans[i];
16851e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            final int start = spanned.getSpanStart(span);
16861e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            final int end = spanned.getSpanEnd(span);
16871e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne
16881e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            if (start == end) {
16891e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                if (copy == null) {
16901e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    copy = (T[]) Array.newInstance(klass, spans.length - 1);
16911e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    System.arraycopy(spans, 0, copy, 0, i);
16921e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    count = i;
16931e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                }
16941e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            } else {
16951e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                if (copy != null) {
16961e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    copy[count] = span;
16971e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    count++;
16981e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                }
16991e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            }
17001e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        }
17011e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne
17021e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        if (copy != null) {
17031e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            T[] result = (T[]) Array.newInstance(klass, count);
17041e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            System.arraycopy(copy, 0, result, 0, count);
17051e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            return result;
17061e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        } else {
17071e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            return spans;
17081e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        }
17091e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne    }
17101e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne
17116c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    /**
17126c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * Pack 2 int values into a long, useful as a return value for a range
17136c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @see #unpackRangeStartFromLong(long)
17146c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @see #unpackRangeEndFromLong(long)
17156c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @hide
17166c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     */
17176c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    public static long packRangeInLong(int start, int end) {
17186c488de023a4797069673dc619c1a4096079ea9eGilles Debunne        return (((long) start) << 32) | end;
17196c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    }
17206c488de023a4797069673dc619c1a4096079ea9eGilles Debunne
17216c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    /**
17226c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * Get the start value from a range packed in a long by {@link #packRangeInLong(int, int)}
17236c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @see #unpackRangeEndFromLong(long)
17246c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @see #packRangeInLong(int, int)
17256c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @hide
17266c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     */
17276c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    public static int unpackRangeStartFromLong(long range) {
17286c488de023a4797069673dc619c1a4096079ea9eGilles Debunne        return (int) (range >>> 32);
17296c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    }
17306c488de023a4797069673dc619c1a4096079ea9eGilles Debunne
17316c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    /**
17326c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * Get the end value from a range packed in a long by {@link #packRangeInLong(int, int)}
17336c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @see #unpackRangeStartFromLong(long)
17346c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @see #packRangeInLong(int, int)
17356c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     * @hide
17366c488de023a4797069673dc619c1a4096079ea9eGilles Debunne     */
17376c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    public static int unpackRangeEndFromLong(long range) {
17386c488de023a4797069673dc619c1a4096079ea9eGilles Debunne        return (int) (range & 0x00000000FFFFFFFFL);
17396c488de023a4797069673dc619c1a4096079ea9eGilles Debunne    }
17406c488de023a4797069673dc619c1a4096079ea9eGilles Debunne
1741d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    /**
1742d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * Return the layout direction for a given Locale
1743d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     *
1744d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * @param locale the Locale for which we want the layout direction. Can be null.
1745d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * @return the layout direction. This may be one of:
1746d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * {@link android.view.View#LAYOUT_DIRECTION_LTR} or
1747d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * {@link android.view.View#LAYOUT_DIRECTION_RTL}.
1748d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     *
1749d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * Be careful: this code will need to be updated when vertical scripts will be supported
1750d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     */
1751d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    public static int getLayoutDirectionFromLocale(Locale locale) {
1752d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio        if (locale != null && !locale.equals(Locale.ROOT)) {
175331e6b1445fc680f0ae79d1a71cf05d1cbaa0c0ecNarayan Kamath            final String scriptSubtag = ICU.addLikelySubtags(locale).getScript();
1754d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio            if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
1755d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio
1756d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio            if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
1757d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio                    scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
1758d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio                return View.LAYOUT_DIRECTION_RTL;
1759d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio            }
1760d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio        }
1761d8415f4bf061000b049143b4f48b96b2005450bbAmith Yamasani        // If forcing into RTL layout mode, return RTL as default, else LTR
1762d8415f4bf061000b049143b4f48b96b2005450bbAmith Yamasani        return SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false)
1763d8415f4bf061000b049143b4f48b96b2005450bbAmith Yamasani                ? View.LAYOUT_DIRECTION_RTL
1764d8415f4bf061000b049143b4f48b96b2005450bbAmith Yamasani                : View.LAYOUT_DIRECTION_LTR;
1765d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    }
1766d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio
1767d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    /**
1768d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * Fallback algorithm to detect the locale direction. Rely on the fist char of the
1769d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * localized locale name. This will not work if the localized locale name is in English
1770d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * (this is the case for ICU 4.4 and "Urdu" script)
1771d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     *
1772d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * @param locale
1773d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * @return the layout direction. This may be one of:
1774d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * {@link View#LAYOUT_DIRECTION_LTR} or
1775d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * {@link View#LAYOUT_DIRECTION_RTL}.
1776d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     *
1777d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * Be careful: this code will need to be updated when vertical scripts will be supported
1778d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     *
1779d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     * @hide
1780d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio     */
1781d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    private static int getLayoutDirectionFromFirstChar(Locale locale) {
1782d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio        switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
1783d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
1784d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
1785d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio                return View.LAYOUT_DIRECTION_RTL;
1786d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio
1787d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
1788d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio            default:
1789d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio                return View.LAYOUT_DIRECTION_LTR;
1790d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio        }
1791d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    }
1792d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio
17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Object sLock = new Object();
1794d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static char[] sTemp = null;
1796cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio
1797cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio    private static String[] EMPTY_STRING_ARRAY = new String[]{};
1798cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio
1799cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio    private static final char ZWNBS_CHAR = '\uFEFF';
1800d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio
1801d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    private static String ARAB_SCRIPT_SUBTAG = "Arab";
1802d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio    private static String HEBR_SCRIPT_SUBTAG = "Hebr";
18039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1804