StaticLayout_Delegate.java revision f3284cbd38989279a116e0082b7ab5b987c13388
1ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptapackage android.text;
2ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
34ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport com.android.annotations.NonNull;
4e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perezimport com.android.layoutlib.bridge.impl.DelegateManager;
5ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate;
6ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
7f3284cbd38989279a116e0082b7ab5b987c13388Diego Perezimport android.graphics.BidiRenderer;
8f3284cbd38989279a116e0082b7ab5b987c13388Diego Perezimport android.graphics.Paint;
9f3284cbd38989279a116e0082b7ab5b987c13388Diego Perezimport android.graphics.Paint_Delegate;
10f3284cbd38989279a116e0082b7ab5b987c13388Diego Perezimport android.graphics.RectF;
114ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport android.text.StaticLayout.LineBreaks;
124ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport android.text.Primitive.PrimitiveType;
134ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
144ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.ArrayList;
15f3284cbd38989279a116e0082b7ab5b987c13388Diego Perezimport java.util.Arrays;
164ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.List;
17ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
18ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport com.ibm.icu.text.BreakIterator;
19ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport com.ibm.icu.util.ULocale;
20ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport javax.swing.text.Segment;
21ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
22ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta/**
23ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
244ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * <p/>
258a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
26ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta * by calls to methods of the same name in this delegate class.
27ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta *
28ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta */
29ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptapublic class StaticLayout_Delegate {
30ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
314ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_SPACE     = 0x20;
324ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_TAB       = 0x09;
334ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_NEWLINE   = 0x0A;
344ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_ZWSP      = 0x200B;  // Zero width space.
354ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
36e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    // ---- Builder delegate manager ----
37e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    private static final DelegateManager<Builder> sBuilderManager =
38e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        new DelegateManager<Builder>(Builder.class);
39e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez
40ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta    @LayoutlibDelegate
41f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    /*package*/ static int nComputeLineBreaks(long nativeBuilder,
424ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int length, float firstWidth, int firstWidthLineCount, float restWidth,
434ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
444ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) {
454ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
46e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
474ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // compute all possible breakpoints.
48e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale));
49f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        it.setText(new Segment(builder.mText, 0, length));
504ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // average word length in english is 5. So, initialize the possible breaks with a guess.
514ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
524ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        int loc;
534ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        it.first();
544ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        while ((loc = it.next()) != BreakIterator.DONE) {
554ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            breaks.add(loc);
56ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta        }
574ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
584ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
594ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
60f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        List<Primitive> primitives = computePrimitives(builder.mText, builder.mWidths, length, breaks);
614ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        LineBreaker lineBreaker;
624ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        if (optimize) {
634ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator);
644ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        } else {
654ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            lineBreaker = new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator);
66ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta        }
674ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        lineBreaker.computeBreaks(recycle);
684ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        return recycle.breaks.length;
69ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta    }
70ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
714ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    /**
724ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta     * Compute metadata each character - things which help in deciding if it's possible to break
734ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta     * at a point or not.
744ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta     */
754ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    @NonNull
764ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths,
774ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int length, @NonNull List<Integer> breaks) {
784ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // Initialize the list with a guess of the number of primitives:
794ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars)
804ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833)));
814ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        int breaksSize = breaks.size();
824ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        int breakIndex = 0;
834ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        for (int i = 0; i < length; i++) {
844ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            char c = text[i];
854ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            if (c == CHAR_SPACE || c == CHAR_ZWSP) {
864ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i]));
874ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            } else if (c == CHAR_TAB) {
884ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i));
894ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            } else if (c != CHAR_NEWLINE) {
904ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                while (breakIndex < breaksSize && breaks.get(breakIndex) < i) {
914ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    breakIndex++;
924ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                }
934ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                Primitive p;
944ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                if (widths[i] != 0) {
954ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    if (breakIndex < breaksSize && breaks.get(breakIndex) == i) {
964ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                        p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0);
974ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    } else {
984ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                        p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0);
994ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    }
1004ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    primitives.add(p);
1014ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                }
1024ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
1034ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i]));
1044ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            }
1054ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        }
1064ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // final break at end of everything
1074ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        primitives.add(
1084ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY));
1094ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        return primitives;
110ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta    }
111e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez
112e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    @LayoutlibDelegate
113e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    /*package*/ static long nNewBuilder() {
114e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        return sBuilderManager.addNewDelegate(new Builder());
115e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    }
116e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez
117e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    @LayoutlibDelegate
118e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    /*package*/ static void nFinishBuilder(long nativeBuilder) {
119e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    }
120e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez
121e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    @LayoutlibDelegate
122e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    /*package*/ static void nFreeBuilder(long nativeBuilder) {
123e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        sBuilderManager.removeJavaReferenceFor(nativeBuilder);
124e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    }
125e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez
126e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    @LayoutlibDelegate
127f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    /*package*/ static void nSetLocale(long nativeBuilder, String locale) {
128e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
129e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        builder.mLocale = locale;
130e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    }
131e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez
132f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    @LayoutlibDelegate
133f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    /*package*/ static void nSetText(long nativeBuilder, char[] text, int length) {
134f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
135f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        builder.mText = text;
136f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        builder.mWidths = new float[length];
137f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    }
138f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
139f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
140f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    @LayoutlibDelegate
141f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    /*package*/ static float nAddStyleRun(long nativeBuilder, long nativePaint, long nativeTypeface,
142f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez            int start, int end, boolean isRtl) {
143f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
144f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
145f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
146f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        return measureText(nativePaint, builder.mText, start, end - start, builder.mWidths, bidiFlags);
147f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    }
148f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
149f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
150f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    @LayoutlibDelegate
151f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    /*package*/ static void nAddMeasuredRun(long nativeBuilder, int start, int end, float[] widths) {
152f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
153f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        System.arraycopy(widths, start, builder.mWidths, start, end - start);
154f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    }
155f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
156f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    @LayoutlibDelegate
157f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    /*package*/ static void nAddReplacementRun(long nativeBuilder, int start, int end, float width) {
158f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
159f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        builder.mWidths[start] = width;
160f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        Arrays.fill(builder.mWidths, start + 1, end, 0.0f);
161f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    }
162f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
163f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    @LayoutlibDelegate
164f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    /*package*/ static void nGetWidths(long nativeBuilder, float[] floatsArray) {
165f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
166f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        System.arraycopy(builder.mWidths, 0, floatsArray, 0, builder.mWidths.length);
167f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    }
168f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
169f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    private static float measureText(long nativePaint, char []text, int index, int count,
170f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez            float[] widths, int bidiFlags) {
171f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
172f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        RectF bounds = new BidiRenderer(null, paint, text)
173f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez            .renderText(index, index + count, bidiFlags, widths, 0, false);
174f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        return bounds.right - bounds.left;
175f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez    }
176f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez
177e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    /**
178f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez     * Java representation of the native Builder class.
179e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez     */
180e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    static class Builder {
181e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez        String mLocale;
182f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        char[] mText;
183f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez        float[] mWidths;
184e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez    }
185ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta}
186