• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package android.text;
2 
3 import com.android.layoutlib.bridge.impl.DelegateManager;
4 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
5 
6 import android.annotation.NonNull;
7 import android.annotation.Nullable;
8 import android.icu.text.BreakIterator;
9 import android.text.Layout.BreakStrategy;
10 import android.text.Layout.HyphenationFrequency;
11 import android.text.Primitive.PrimitiveType;
12 import android.text.StaticLayout.LineBreaks;
13 
14 import java.text.CharacterIterator;
15 import java.util.ArrayList;
16 import java.util.List;
17 
18 import 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  */
27 public 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
nInit( @reakStrategy int breakStrategy, @HyphenationFrequency int hyphenationFrequency, boolean isJustified, @Nullable int[] indents, @Nullable int[] leftPaddings, @Nullable int[] rightPaddings)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
nFinish(long nativePtr)52     /*package*/ static void nFinish(long nativePtr) {
53         sBuilderManager.removeJavaReferenceFor(nativePtr);
54     }
55 
56     @LayoutlibDelegate
nComputeLineBreaks( long nativePtr, @NonNull char[] text, long measuredTextPtr, int length, float firstWidth, int firstWidthLineCount, float restWidth, @Nullable int[] variableTabStops, int defaultTabStop, int indentsOffset, @NonNull LineBreaks recycle, int recycleLength, @NonNull int[] recycleBreaks, @NonNull float[] recycleWidths, @NonNull float[] recycleAscents, @NonNull float[] recycleDescents, @NonNull int[] recycleFlags, @NonNull float[] charWidths)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
computePrimitives(@onNull char[] text, @NonNull float[] widths, int length, @NonNull List<Integer> breaks)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 
Run(int start, int end)186         Run(int start, int end) {
187             mStart = start;
188             mEnd = end;
189         }
190 
addTo(Builder builder)191         abstract void addTo(Builder builder);
192     }
193 }
194