1package android.text; 2 3import com.android.layoutlib.bridge.impl.DelegateManager; 4import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 5 6import android.annotation.NonNull; 7import android.annotation.Nullable; 8import android.icu.text.BreakIterator; 9import android.text.Layout.BreakStrategy; 10import android.text.Layout.HyphenationFrequency; 11import android.text.Primitive.PrimitiveType; 12import android.text.StaticLayout.LineBreaks; 13 14import java.text.CharacterIterator; 15import java.util.ArrayList; 16import java.util.List; 17 18import javax.swing.text.Segment; 19 20/** 21 * Delegate that provides implementation for native methods in {@link android.text.StaticLayout} 22 * <p/> 23 * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced 24 * by calls to methods of the same name in this delegate class. 25 * 26 */ 27public class StaticLayout_Delegate { 28 29 private static final char CHAR_SPACE = 0x20; 30 private static final char CHAR_TAB = 0x09; 31 private static final char CHAR_NEWLINE = 0x0A; 32 private static final char CHAR_ZWSP = 0x200B; // Zero width space. 33 34 // ---- Builder delegate manager ---- 35 private static final DelegateManager<Builder> sBuilderManager = 36 new DelegateManager<Builder>(Builder.class); 37 38 @LayoutlibDelegate 39 /*package*/ static long nInit( 40 @BreakStrategy int breakStrategy, 41 @HyphenationFrequency int hyphenationFrequency, 42 boolean isJustified, 43 @Nullable int[] indents, 44 @Nullable int[] leftPaddings, 45 @Nullable int[] rightPaddings) { 46 Builder builder = new Builder(); 47 builder.mBreakStrategy = breakStrategy; 48 return sBuilderManager.addNewDelegate(builder); 49 } 50 51 @LayoutlibDelegate 52 /*package*/ static void nFinish(long nativePtr) { 53 sBuilderManager.removeJavaReferenceFor(nativePtr); 54 } 55 56 @LayoutlibDelegate 57 /*package*/ static int nComputeLineBreaks( 58 /* non zero */ long nativePtr, 59 60 // Inputs 61 @NonNull char[] text, 62 long measuredTextPtr, 63 int length, 64 float firstWidth, 65 int firstWidthLineCount, 66 float restWidth, 67 @Nullable int[] variableTabStops, 68 int defaultTabStop, 69 int indentsOffset, 70 71 // Outputs 72 @NonNull LineBreaks recycle, 73 int recycleLength, 74 @NonNull int[] recycleBreaks, 75 @NonNull float[] recycleWidths, 76 @NonNull float[] recycleAscents, 77 @NonNull float[] recycleDescents, 78 @NonNull int[] recycleFlags, 79 @NonNull float[] charWidths) { 80 Builder builder = sBuilderManager.getDelegate(nativePtr); 81 if (builder == null) { 82 return 0; 83 } 84 85 builder.mText = text; 86 builder.mWidths = new float[length]; 87 builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth); 88 builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop); 89 90 MeasuredParagraph_Delegate.computeRuns(measuredTextPtr, builder); 91 92 // compute all possible breakpoints. 93 BreakIterator it = BreakIterator.getLineInstance(); 94 it.setText((CharacterIterator) new Segment(builder.mText, 0, length)); 95 96 // average word length in english is 5. So, initialize the possible breaks with a guess. 97 List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d)); 98 int loc; 99 it.first(); 100 while ((loc = it.next()) != BreakIterator.DONE) { 101 breaks.add(loc); 102 } 103 104 List<Primitive> primitives = 105 computePrimitives(builder.mText, builder.mWidths, length, breaks); 106 switch (builder.mBreakStrategy) { 107 case Layout.BREAK_STRATEGY_SIMPLE: 108 builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth, 109 builder.mTabStopCalculator); 110 break; 111 case Layout.BREAK_STRATEGY_HIGH_QUALITY: 112 // TODO 113// break; 114 case Layout.BREAK_STRATEGY_BALANCED: 115 builder.mLineBreaker = new OptimizingLineBreaker(primitives, builder.mLineWidth, 116 builder.mTabStopCalculator); 117 break; 118 default: 119 assert false : "Unknown break strategy: " + builder.mBreakStrategy; 120 builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth, 121 builder.mTabStopCalculator); 122 } 123 builder.mLineBreaker.computeBreaks(recycle); 124 System.arraycopy(builder.mWidths, 0, charWidths, 0, builder.mWidths.length); 125 return recycle.breaks.length; 126 } 127 128 /** 129 * Compute metadata each character - things which help in deciding if it's possible to break 130 * at a point or not. 131 */ 132 @NonNull 133 private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths, 134 int length, @NonNull List<Integer> breaks) { 135 // Initialize the list with a guess of the number of primitives: 136 // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars) 137 List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833))); 138 int breaksSize = breaks.size(); 139 int breakIndex = 0; 140 for (int i = 0; i < length; i++) { 141 char c = text[i]; 142 if (c == CHAR_SPACE || c == CHAR_ZWSP) { 143 primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i])); 144 } else if (c == CHAR_TAB) { 145 primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i)); 146 } else if (c != CHAR_NEWLINE) { 147 while (breakIndex < breaksSize && breaks.get(breakIndex) < i) { 148 breakIndex++; 149 } 150 Primitive p; 151 if (widths[i] != 0) { 152 if (breakIndex < breaksSize && breaks.get(breakIndex) == i) { 153 p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0); 154 } else { 155 p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0); 156 } 157 primitives.add(p); 158 } 159 160 primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i])); 161 } 162 } 163 // final break at end of everything 164 primitives.add( 165 PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY)); 166 return primitives; 167 } 168 169 // TODO: Rename to LineBreakerRef and move everything other than LineBreaker to LineBreaker. 170 /** 171 * Java representation of the native Builder class. 172 */ 173 public static class Builder { 174 char[] mText; 175 float[] mWidths; 176 private LineBreaker mLineBreaker; 177 private int mBreakStrategy; 178 private LineWidth mLineWidth; 179 private TabStops mTabStopCalculator; 180 } 181 182 public abstract static class Run { 183 int mStart; 184 int mEnd; 185 186 Run(int start, int end) { 187 mStart = start; 188 mEnd = end; 189 } 190 191 abstract void addTo(Builder builder); 192 } 193} 194