1ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptapackage android.text; 2ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta 3e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perezimport com.android.layoutlib.bridge.impl.DelegateManager; 4ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate; 5ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta 6442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.NonNull; 7345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillardimport android.annotation.Nullable; 89fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Guptaimport android.icu.text.BreakIterator; 9345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillardimport android.text.Layout.BreakStrategy; 10345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillardimport android.text.Layout.HyphenationFrequency; 114ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport android.text.Primitive.PrimitiveType; 129fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Guptaimport android.text.StaticLayout.LineBreaks; 134ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 146b06ef9725495cf071deec3bad76aff32c59b39eVictor Changimport java.text.CharacterIterator; 154ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.ArrayList; 164ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Guptaimport java.util.List; 17ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta 18ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptaimport javax.swing.text.Segment; 19ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta 20ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta/** 21ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta * Delegate that provides implementation for native methods in {@link android.text.StaticLayout} 224ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * <p/> 238a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced 24ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta * by calls to methods of the same name in this delegate class. 25ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta * 26ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta */ 27ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Guptapublic class StaticLayout_Delegate { 28ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta 294ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static final char CHAR_SPACE = 0x20; 304ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static final char CHAR_TAB = 0x09; 314ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static final char CHAR_NEWLINE = 0x0A; 324ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static final char CHAR_ZWSP = 0x200B; // Zero width space. 334ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 34e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez // ---- Builder delegate manager ---- 35e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez private static final DelegateManager<Builder> sBuilderManager = 36e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez new DelegateManager<Builder>(Builder.class); 37e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez 38ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta @LayoutlibDelegate 39345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard /*package*/ static long nInit( 40345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @BreakStrategy int breakStrategy, 41345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @HyphenationFrequency int hyphenationFrequency, 42345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard boolean isJustified, 43345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @Nullable int[] indents, 44345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @Nullable int[] leftPaddings, 45345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @Nullable int[] rightPaddings) { 46345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard Builder builder = new Builder(); 47345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard builder.mBreakStrategy = breakStrategy; 48345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard return sBuilderManager.addNewDelegate(builder); 499fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta } 509fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta 519fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta @LayoutlibDelegate 52345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard /*package*/ static void nFinish(long nativePtr) { 53345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard sBuilderManager.removeJavaReferenceFor(nativePtr); 549fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta } 559fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta 569fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta @LayoutlibDelegate 57345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard /*package*/ static int nComputeLineBreaks( 58345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard /* non zero */ long nativePtr, 599fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta 60345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard // Inputs 61345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull char[] text, 620b516c5c0cac7df4d540a9fbf0beab415837b42bJerome Gaillard long measuredTextPtr, 63345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard int length, 64345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard float firstWidth, 65345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard int firstWidthLineCount, 66345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard float restWidth, 67345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @Nullable int[] variableTabStops, 68345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard int defaultTabStop, 69345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard int indentsOffset, 70345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard 71345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard // Outputs 72345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull LineBreaks recycle, 73345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard int recycleLength, 74345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull int[] recycleBreaks, 75345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull float[] recycleWidths, 76345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull float[] recycleAscents, 77345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull float[] recycleDescents, 78345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull int[] recycleFlags, 79345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard @NonNull float[] charWidths) { 80345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard Builder builder = sBuilderManager.getDelegate(nativePtr); 819fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta if (builder == null) { 829fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta return 0; 839fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta } 847169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta 85345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard builder.mText = text; 86345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard builder.mWidths = new float[length]; 87345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth); 88345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop); 89345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard 90f9ed408cb244e6924d51c50c99cdb5186a9c6ad4Jerome Gaillard MeasuredParagraph_Delegate.computeRuns(measuredTextPtr, builder); 91345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard 927169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta // compute all possible breakpoints. 93009184dd1182a3bcd25a18302ad34a07c3e6be42Jerome Gaillard BreakIterator it = BreakIterator.getLineInstance(); 946b06ef9725495cf071deec3bad76aff32c59b39eVictor Chang it.setText((CharacterIterator) new Segment(builder.mText, 0, length)); 957169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta 967169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta // average word length in english is 5. So, initialize the possible breaks with a guess. 977169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d)); 987169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta int loc; 997169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta it.first(); 1007169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta while ((loc = it.next()) != BreakIterator.DONE) { 1017169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta breaks.add(loc); 1027169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta } 1037169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta 1047169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta List<Primitive> primitives = 1057169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta computePrimitives(builder.mText, builder.mWidths, length, breaks); 1067169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta switch (builder.mBreakStrategy) { 1077169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta case Layout.BREAK_STRATEGY_SIMPLE: 1087169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth, 1097169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta builder.mTabStopCalculator); 1107169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta break; 1117169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta case Layout.BREAK_STRATEGY_HIGH_QUALITY: 1127169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta // TODO 1137169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta// break; 1147169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta case Layout.BREAK_STRATEGY_BALANCED: 1157169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta builder.mLineBreaker = new OptimizingLineBreaker(primitives, builder.mLineWidth, 1167169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta builder.mTabStopCalculator); 1177169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta break; 1187169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta default: 1190609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez assert false : "Unknown break strategy: " + builder.mBreakStrategy; 1200609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth, 1210609785e6f839bfd27e33fa2630e6a1c12be9a20Diego Perez builder.mTabStopCalculator); 1227169c14107fc4291342b6211fff01723996fd351Deepanshu Gupta } 1239fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta builder.mLineBreaker.computeBreaks(recycle); 124b28e813fd8bd9a9e330ea1803f5b153b6fc7d4e0Jerome Gaillard System.arraycopy(builder.mWidths, 0, charWidths, 0, builder.mWidths.length); 1254ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return recycle.breaks.length; 126ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta } 127ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta 1284ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta /** 1294ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * Compute metadata each character - things which help in deciding if it's possible to break 1304ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta * at a point or not. 1314ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta */ 1324ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta @NonNull 1334ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths, 1344ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int length, @NonNull List<Integer> breaks) { 1354ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // Initialize the list with a guess of the number of primitives: 1364ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars) 1374ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833))); 1384ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int breaksSize = breaks.size(); 1394ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta int breakIndex = 0; 1404ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta for (int i = 0; i < length; i++) { 1414ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta char c = text[i]; 1424ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (c == CHAR_SPACE || c == CHAR_ZWSP) { 1434ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i])); 1444ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else if (c == CHAR_TAB) { 1454ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i)); 1464ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else if (c != CHAR_NEWLINE) { 1474ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta while (breakIndex < breaksSize && breaks.get(breakIndex) < i) { 1484ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta breakIndex++; 1494ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1504ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta Primitive p; 1514ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (widths[i] != 0) { 1524ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta if (breakIndex < breaksSize && breaks.get(breakIndex) == i) { 1534ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0); 1544ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } else { 1554ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0); 1564ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1574ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta primitives.add(p); 1584ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1594ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta 1604ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i])); 1614ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1624ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta } 1634ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta // final break at end of everything 1644ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta primitives.add( 1654ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY)); 1664ef9b507c2c1b73805533a86d935d637493d5903Deepanshu Gupta return primitives; 167ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta } 168e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez 1699fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta // TODO: Rename to LineBreakerRef and move everything other than LineBreaker to LineBreaker. 170e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez /** 171f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez * Java representation of the native Builder class. 172e8685f6153d281f24d1d2cbfa5942f40a5b461b1Diego Perez */ 1730b516c5c0cac7df4d540a9fbf0beab415837b42bJerome Gaillard public static class Builder { 174f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez char[] mText; 175f3284cbd38989279a116e0082b7ab5b987c13388Diego Perez float[] mWidths; 176345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard private LineBreaker mLineBreaker; 177345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard private int mBreakStrategy; 178345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard private LineWidth mLineWidth; 179345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard private TabStops mTabStopCalculator; 180345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard } 181345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard 1820b516c5c0cac7df4d540a9fbf0beab415837b42bJerome Gaillard public abstract static class Run { 183345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard int mStart; 184345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard int mEnd; 185345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard 186345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard Run(int start, int end) { 187345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard mStart = start; 188345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard mEnd = end; 189345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard } 190345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard 191345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard abstract void addTo(Builder builder); 192345d3cef8e40f59abeaba5402f51d3068b229bbcJerome Gaillard } 193ae0b1a2ebde9984c2e75650b7dcfa7449d1a8833Deepanshu Gupta} 194