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