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.content.res;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guyimport android.graphics.Color;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.*;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.*;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.SparseArray;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Typeface;
272269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn
28a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guyimport java.util.Arrays;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Conveniences for retrieving data out of a compiled string resource.
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide}
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectfinal class StringBlock {
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "AssetManager";
37a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy    private static final boolean localLOGV = false;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
39896043d67d3ac75760bd99db8a1561e31ebee1e1Ashok Bhat    private final long mNative;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final boolean mUseSparse;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final boolean mOwnsNative;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private CharSequence[] mStrings;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SparseArray<CharSequence> mSparseStrings;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    StyleIDs mStyleIDs = null;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StringBlock(byte[] data, boolean useSparse) {
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mNative = nativeCreate(data, 0, data.length);
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mUseSparse = useSparse;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOwnsNative = true;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (localLOGV) Log.v(TAG, "Created string block " + this
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + ": " + nativeGetSize(mNative));
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StringBlock(byte[] data, int offset, int size, boolean useSparse) {
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mNative = nativeCreate(data, offset, size);
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mUseSparse = useSparse;
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOwnsNative = true;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (localLOGV) Log.v(TAG, "Created string block " + this
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + ": " + nativeGetSize(mNative));
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public CharSequence get(int idx) {
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (this) {
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mStrings != null) {
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                CharSequence res = mStrings[idx];
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (res != null) {
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return res;
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mSparseStrings != null) {
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                CharSequence res = mSparseStrings.get(idx);
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (res != null) {
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return res;
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final int num = nativeGetSize(mNative);
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mUseSparse && num > 250) {
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSparseStrings = new SparseArray<CharSequence>();
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mStrings = new CharSequence[num];
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String str = nativeGetString(mNative, idx);
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            CharSequence res = str;
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int[] style = nativeGetStyle(mNative, idx);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (localLOGV) Log.v(TAG, "Got string: " + str);
86a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            if (localLOGV) Log.v(TAG, "Got styles: " + Arrays.toString(style));
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (style != null) {
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mStyleIDs == null) {
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mStyleIDs = new StyleIDs();
90ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                }
91ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson
92ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                // the style array is a flat array of <type, start, end> hence
93ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                // the magic constant 3.
94ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                for (int styleIndex = 0; styleIndex < style.length; styleIndex += 3) {
95ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    int styleId = style[styleIndex];
96ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson
97ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    if (styleId == mStyleIDs.boldId || styleId == mStyleIDs.italicId
98ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                            || styleId == mStyleIDs.underlineId || styleId == mStyleIDs.ttId
99ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                            || styleId == mStyleIDs.bigId || styleId == mStyleIDs.smallId
100ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                            || styleId == mStyleIDs.subId || styleId == mStyleIDs.supId
101ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                            || styleId == mStyleIDs.strikeId || styleId == mStyleIDs.listItemId
102ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                            || styleId == mStyleIDs.marqueeId) {
103ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        // id already found skip to next style
104ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        continue;
105ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    }
106ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson
107ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    String styleTag = nativeGetString(mNative, styleId);
108ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson
109ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    if (styleTag.equals("b")) {
110ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.boldId = styleId;
111ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("i")) {
112ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.italicId = styleId;
113ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("u")) {
114ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.underlineId = styleId;
115ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("tt")) {
116ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.ttId = styleId;
117ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("big")) {
118ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.bigId = styleId;
119ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("small")) {
120ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.smallId = styleId;
121ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("sup")) {
122ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.supId = styleId;
123ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("sub")) {
124ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.subId = styleId;
125ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("strike")) {
126ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.strikeId = styleId;
127ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("li")) {
128ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.listItemId = styleId;
129ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    } else if (styleTag.equals("marquee")) {
130ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                        mStyleIDs.marqueeId = styleId;
131ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson                    }
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                res = applyStyles(str, style, mStyleIDs);
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mStrings != null) mStrings[idx] = res;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else mSparseStrings.put(idx, res);
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return res;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void finalize() throws Throwable {
143a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        try {
144a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            super.finalize();
145a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        } finally {
146a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            if (mOwnsNative) {
147a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                nativeDestroy(mNative);
148a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            }
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final class StyleIDs {
153ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int boldId = -1;
154ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int italicId = -1;
155ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int underlineId = -1;
156ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int ttId = -1;
157ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int bigId = -1;
158ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int smallId = -1;
159ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int subId = -1;
160ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int supId = -1;
161ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int strikeId = -1;
162ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int listItemId = -1;
163ac4a181d2e02f2dd375c61ce231d35e50d0d1836Peter Eliasson        private int marqueeId = -1;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private CharSequence applyStyles(String str, int[] style, StyleIDs ids) {
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (style.length == 0)
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return str;
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpannableString buffer = new SpannableString(str);
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i=0;
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < style.length) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int type = style[i];
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (localLOGV) Log.v(TAG, "Applying style span id=" + type
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + ", start=" + style[i+1] + ", end=" + style[i+2]);
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (type == ids.boldId) {
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new StyleSpan(Typeface.BOLD),
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.italicId) {
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new StyleSpan(Typeface.ITALIC),
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.underlineId) {
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new UnderlineSpan(),
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.ttId) {
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new TypefaceSpan("monospace"),
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.bigId) {
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new RelativeSizeSpan(1.25f),
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.smallId) {
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new RelativeSizeSpan(0.8f),
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.subId) {
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new SubscriptSpan(),
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.supId) {
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new SuperscriptSpan(),
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.strikeId) {
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(new StrikethroughSpan(),
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.listItemId) {
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                addParagraphSpan(buffer, new BulletSpan(10),
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                style[i+1], style[i+2]+1);
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (type == ids.marqueeId) {
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buffer.setSpan(TextUtils.TruncateAt.MARQUEE,
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               style[i+1], style[i+2]+1,
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               Spannable.SPAN_INCLUSIVE_INCLUSIVE);
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String tag = nativeGetString(mNative, type);
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (tag.startsWith("font;")) {
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String sub;
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sub = subtag(tag, ";height=");
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (sub != null) {
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int size = Integer.parseInt(sub);
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        addParagraphSpan(buffer, new Height(size),
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       style[i+1], style[i+2]+1);
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sub = subtag(tag, ";size=");
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (sub != null) {
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int size = Integer.parseInt(sub);
237a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                        buffer.setSpan(new AbsoluteSizeSpan(size, true),
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       style[i+1], style[i+2]+1,
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sub = subtag(tag, ";fgcolor=");
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (sub != null) {
244a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                        buffer.setSpan(getColor(sub, true),
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       style[i+1], style[i+2]+1,
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
249a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    sub = subtag(tag, ";color=");
250a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    if (sub != null) {
251a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                        buffer.setSpan(getColor(sub, true),
252a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                                style[i+1], style[i+2]+1,
253a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
254a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    }
255a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sub = subtag(tag, ";bgcolor=");
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (sub != null) {
258a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                        buffer.setSpan(getColor(sub, false),
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       style[i+1], style[i+2]+1,
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
262a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy
263a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    sub = subtag(tag, ";face=");
264a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    if (sub != null) {
265a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                        buffer.setSpan(new TypefaceSpan(sub),
266a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                                style[i+1], style[i+2]+1,
267a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
268a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    }
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (tag.startsWith("a;")) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String sub;
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sub = subtag(tag, ";href=");
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (sub != null) {
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        buffer.setSpan(new URLSpan(sub),
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       style[i+1], style[i+2]+1,
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (tag.startsWith("annotation;")) {
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int len = tag.length();
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int next;
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int t = tag.indexOf(';'); t < len; t = next) {
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int eq = tag.indexOf('=', t);
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (eq < 0) {
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        next = tag.indexOf(';', eq);
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (next < 0) {
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            next = len;
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        String key = tag.substring(t + 1, eq);
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        String value = tag.substring(eq + 1, next);
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        buffer.setSpan(new Annotation(key, value),
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       style[i+1], style[i+2]+1,
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            i += 3;
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new SpannedString(buffer);
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
309a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     * Returns a span for the specified color string representation.
310a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     * If the specified string does not represent a color (null, empty, etc.)
311a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     * the color black is returned instead.
312a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     *
313a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     * @param color The color as a string. Can be a resource reference,
314a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     *              HTML hexadecimal, octal or a name
315a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     * @param foreground True if the color will be used as the foreground color,
316a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     *                   false otherwise
317a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     *
318a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     * @return A CharacterStyle
319a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     *
320a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     * @see Color#getHtmlColor(String)
321a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy     */
322a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy    private static CharacterStyle getColor(String color, boolean foreground) {
323a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        int c = 0xff000000;
324a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy
325a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        if (!TextUtils.isEmpty(color)) {
326a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            if (color.startsWith("@")) {
327a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                Resources res = Resources.getSystem();
328a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                String name = color.substring(1);
329a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                int colorRes = res.getIdentifier(name, "color", "android");
330a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                if (colorRes != 0) {
331a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    ColorStateList colors = res.getColorStateList(colorRes);
332a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    if (foreground) {
333a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                        return new TextAppearanceSpan(null, 0, 0, colors, null);
334a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    } else {
335a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                        c = colors.getDefaultColor();
336a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                    }
337a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                }
338a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            } else {
339a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy                c = Color.getHtmlColor(color);
340a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            }
341a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        }
342a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy
343a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        if (foreground) {
344a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            return new ForegroundColorSpan(c);
345a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        } else {
346a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy            return new BackgroundColorSpan(c);
347a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy        }
348a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy    }
349a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy
350a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy    /**
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If a translator has messed up the edges of paragraph-level markup,
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * fix it to actually cover the entire paragraph that it is attached to
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * instead of just whatever range they put it on.
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void addParagraphSpan(Spannable buffer, Object what,
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         int start, int end) {
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = buffer.length();
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start != 0 && start != len && buffer.charAt(start - 1) != '\n') {
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (start--; start > 0; start--) {
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (buffer.charAt(start - 1) == '\n') {
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end != 0 && end != len && buffer.charAt(end - 1) != '\n') {
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (end++; end < len; end++) {
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (buffer.charAt(end - 1) == '\n') {
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        buffer.setSpan(what, start, end, Spannable.SPAN_PARAGRAPH);
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String subtag(String full, String attribute) {
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = full.indexOf(attribute);
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start < 0) {
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        start += attribute.length();
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end = full.indexOf(';', start);
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end < 0) {
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return full.substring(start);
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return full.substring(start, end);
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Forces the text line to be the specified height, shrinking/stretching
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the ascent if possible, or the descent if shrinking the ascent further
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * will make the text unreadable.
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
399a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer    private static class Height implements LineHeightSpan.WithDensity {
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mSize;
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static float sProportion = 0;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Height(int size) {
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSize = size;
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void chooseHeight(CharSequence text, int start, int end,
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                 int spanstartv, int v,
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                 Paint.FontMetricsInt fm) {
410a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer            // Should not get called, at least not by StaticLayout.
411a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer            chooseHeight(text, start, end, spanstartv, v, fm, null);
412a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer        }
413a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer
414a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer        public void chooseHeight(CharSequence text, int start, int end,
415a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                                 int spanstartv, int v,
416a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                                 Paint.FontMetricsInt fm, TextPaint paint) {
417a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer            int size = mSize;
418a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer            if (paint != null) {
419a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                size *= paint.density;
420a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer            }
421a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer
422a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer            if (fm.bottom - fm.top < size) {
423a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                fm.top = fm.bottom - size;
424a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                fm.ascent = fm.ascent - size;
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (sProportion == 0) {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    /*
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * Calculate what fraction of the nominal ascent
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * the height of a capital letter actually is,
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * so that we won't reduce the ascent to less than
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * that unless we absolutely have to.
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     */
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Paint p = new Paint();
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    p.setTextSize(100);
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Rect r = new Rect();
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    p.getTextBounds("ABCDEFG", 0, 7, r);
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sProportion = (r.top) / p.ascent();
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int need = (int) Math.ceil(-fm.top * sProportion);
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
444a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                if (size - fm.descent >= need) {
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    /*
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * It is safe to shrink the ascent this much.
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     */
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
449a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                    fm.top = fm.bottom - size;
450a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                    fm.ascent = fm.descent - size;
451a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                } else if (size >= need) {
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    /*
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * We can't show all the descent, but we can at least
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * show all the ascent.
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     */
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fm.top = fm.ascent = -need;
458a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                    fm.bottom = fm.descent = fm.top + size;
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    /*
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     * Show as much of the ascent as we can, and no descent.
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     */
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
464a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                    fm.top = fm.ascent = -size;
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fm.bottom = fm.descent = 0;
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create from an existing string block native object.  This is
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * -extremely- dangerous -- only use it if you absolutely know what you
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  are doing!  The given native object must exist for the entire lifetime
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  of this newly creating StringBlock.
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
477896043d67d3ac75760bd99db8a1561e31ebee1e1Ashok Bhat    StringBlock(long obj, boolean useSparse) {
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mNative = obj;
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mUseSparse = useSparse;
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOwnsNative = false;
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (localLOGV) Log.v(TAG, "Created string block " + this
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + ": " + nativeGetSize(mNative));
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
485896043d67d3ac75760bd99db8a1561e31ebee1e1Ashok Bhat    private static native long nativeCreate(byte[] data,
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                 int offset,
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                 int size);
488896043d67d3ac75760bd99db8a1561e31ebee1e1Ashok Bhat    private static native int nativeGetSize(long obj);
489896043d67d3ac75760bd99db8a1561e31ebee1e1Ashok Bhat    private static native String nativeGetString(long obj, int idx);
490896043d67d3ac75760bd99db8a1561e31ebee1e1Ashok Bhat    private static native int[] nativeGetStyle(long obj, int idx);
491896043d67d3ac75760bd99db8a1561e31ebee1e1Ashok Bhat    private static native void nativeDestroy(long obj);
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
493