StaticLayout_Delegate.java revision 4ef9b507c2c1b73805533a86d935d637493d5903
1ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptapackage android.text;
2ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
34ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport com.android.annotations.NonNull;
4ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate;
5ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
64ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport android.text.StaticLayout.LineBreaks;
74ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport android.text.Primitive.PrimitiveType;
84ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
94ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.ArrayList;
104ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.List;
11ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
12ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport com.ibm.icu.text.BreakIterator;
13ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport com.ibm.icu.util.ULocale;
14ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport javax.swing.text.Segment;
15ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
16ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta/**
17ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
184ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * <p/>
194ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * Through the layoutlib_create tool, selected methods of Handler have been replaced by calls to
204ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * methods of the same name in this delegate class.
21ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta */
22ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptapublic class StaticLayout_Delegate {
23ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
244ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_SPACE     = 0x20;
254ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_TAB       = 0x09;
264ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_NEWLINE   = 0x0A;
274ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static final char CHAR_ZWSP      = 0x200B;  // Zero width space.
284ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
29ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta    @LayoutlibDelegate
304ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    /*package*/ static int nComputeLineBreaks(String locale, char[] inputText, float[] widths,
314ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int length, float firstWidth, int firstWidthLineCount, float restWidth,
324ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
334ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) {
344ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
354ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // compute all possible breakpoints.
364ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        BreakIterator it = BreakIterator.getLineInstance(new ULocale(locale));
374ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        it.setText(new Segment(inputText, 0, length));
384ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // average word length in english is 5. So, initialize the possible breaks with a guess.
394ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
404ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        int loc;
414ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        it.first();
424ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        while ((loc = it.next()) != BreakIterator.DONE) {
434ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            breaks.add(loc);
44ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta        }
454ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
464ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
474ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
484ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        List<Primitive> primitives = computePrimitives(inputText, widths, length, breaks);
494ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        LineBreaker lineBreaker;
504ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        if (optimize) {
514ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator);
524ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        } else {
534ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            lineBreaker = new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator);
54ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta        }
554ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        lineBreaker.computeBreaks(recycle);
564ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        return recycle.breaks.length;
57ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta    }
58ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta
594ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    /**
604ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta     * Compute metadata each character - things which help in deciding if it's possible to break
614ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta     * at a point or not.
624ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta     */
634ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    @NonNull
644ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta    private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths,
654ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            int length, @NonNull List<Integer> breaks) {
664ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // Initialize the list with a guess of the number of primitives:
674ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars)
684ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833)));
694ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        int breaksSize = breaks.size();
704ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        int breakIndex = 0;
714ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        for (int i = 0; i < length; i++) {
724ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            char c = text[i];
734ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            if (c == CHAR_SPACE || c == CHAR_ZWSP) {
744ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i]));
754ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            } else if (c == CHAR_TAB) {
764ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i));
774ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            } else if (c != CHAR_NEWLINE) {
784ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                while (breakIndex < breaksSize && breaks.get(breakIndex) < i) {
794ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    breakIndex++;
804ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                }
814ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                Primitive p;
824ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                if (widths[i] != 0) {
834ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    if (breakIndex < breaksSize && breaks.get(breakIndex) == i) {
844ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                        p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0);
854ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    } else {
864ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                        p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0);
874ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    }
884ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                    primitives.add(p);
894ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                }
904ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta
914ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i]));
924ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta            }
934ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        }
944ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        // final break at end of everything
954ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        primitives.add(
964ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta                PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY));
974ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta        return primitives;
98ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta    }
99ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta}
100