OSDN Git Service

Introduce full justification.
authorSeigo Nonaka <nona@google.com>
Mon, 28 Nov 2016 07:24:14 +0000 (16:24 +0900)
committerSeigo Nonaka <nona@google.com>
Thu, 5 Jan 2017 09:14:15 +0000 (18:14 +0900)
Adds a new get/setJustify API to TextView and StaticLayout.Builder for
justification, and fully justifies text when it's enabled.

This is based on a patch by Raph Levien (raph@google.com).

Bug: 31707212
Test: Manually done and CTS will introduced with
      I0f3bbf39d60a66b71b30e1351f7c741208f05dce passes.
Change-Id: Icbfab2faa11a6a0b52e6f0a77a9c9b5ef6e191da

api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/text/DynamicLayout.java
core/java/android/text/Layout.java
core/java/android/text/StaticLayout.java
core/java/android/text/TextLine.java
core/java/android/widget/TextView.java
core/jni/android_text_StaticLayout.cpp

index b017f9d..c2b5576 100644 (file)
@@ -39434,6 +39434,7 @@ package android.text {
     method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
     method public android.text.StaticLayout.Builder setIncludePad(boolean);
     method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setJustify(boolean);
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -48958,6 +48959,7 @@ package android.widget {
     method public boolean getIncludeFontPadding();
     method public android.os.Bundle getInputExtras(boolean);
     method public int getInputType();
+    method public boolean getJustify();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
@@ -49066,6 +49068,7 @@ package android.widget {
     method public void setIncludeFontPadding(boolean);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
+    method public void setJustify(boolean);
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLetterSpacing(float);
     method public void setLineSpacing(float, float);
index a5534de..b193c57 100644 (file)
@@ -42644,6 +42644,7 @@ package android.text {
     method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
     method public android.text.StaticLayout.Builder setIncludePad(boolean);
     method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setJustify(boolean);
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -52528,6 +52529,7 @@ package android.widget {
     method public boolean getIncludeFontPadding();
     method public android.os.Bundle getInputExtras(boolean);
     method public int getInputType();
+    method public boolean getJustify();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
@@ -52636,6 +52638,7 @@ package android.widget {
     method public void setIncludeFontPadding(boolean);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
+    method public void setJustify(boolean);
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLetterSpacing(float);
     method public void setLineSpacing(float, float);
index 5db134a..754aa65 100644 (file)
@@ -39554,6 +39554,7 @@ package android.text {
     method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
     method public android.text.StaticLayout.Builder setIncludePad(boolean);
     method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setJustify(boolean);
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -49260,6 +49261,7 @@ package android.widget {
     method public boolean getIncludeFontPadding();
     method public android.os.Bundle getInputExtras(boolean);
     method public int getInputType();
+    method public boolean getJustify();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
@@ -49368,6 +49370,7 @@ package android.widget {
     method public void setIncludeFontPadding(boolean);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
+    method public void setJustify(boolean);
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLetterSpacing(float);
     method public void setLineSpacing(float, float);
index 9ac8996..1e9deeb 100644 (file)
@@ -85,7 +85,7 @@ public class DynamicLayout extends Layout
         this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
                 spacingmult, spacingadd, includepad,
                 StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE,
-                ellipsize, ellipsizedWidth);
+                false /* justify */, ellipsize, ellipsizedWidth);
     }
 
     /**
@@ -102,7 +102,8 @@ public class DynamicLayout extends Layout
                          int width, Alignment align, TextDirectionHeuristic textDir,
                          float spacingmult, float spacingadd,
                          boolean includepad, int breakStrategy, int hyphenationFrequency,
-                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+                         boolean justify, TextUtils.TruncateAt ellipsize,
+                         int ellipsizedWidth) {
         super((ellipsize == null)
                 ? display
                 : (display instanceof Spanned)
@@ -127,6 +128,7 @@ public class DynamicLayout extends Layout
 
         mIncludePad = includepad;
         mBreakStrategy = breakStrategy;
+        mJustify = justify;
         mHyphenationFrequency = hyphenationFrequency;
 
         /*
@@ -300,7 +302,8 @@ public class DynamicLayout extends Layout
                 .setEllipsizedWidth(mEllipsizedWidth)
                 .setEllipsize(mEllipsizeAt)
                 .setBreakStrategy(mBreakStrategy)
-                .setHyphenationFrequency(mHyphenationFrequency);
+                .setHyphenationFrequency(mHyphenationFrequency)
+                .setJustify(mJustify);
         reflowed.generate(b, false, true);
         int n = reflowed.getLineCount();
         // If the new layout has a blank line at the end, but it is not
@@ -808,6 +811,7 @@ public class DynamicLayout extends Layout
     private TextUtils.TruncateAt mEllipsizeAt;
     private int mBreakStrategy;
     private int mHyphenationFrequency;
+    private boolean mJustify;
 
     private PackedIntVector mInts;
     private PackedObjectVector<Directions> mObjects;
index 3cb81b0..2fc12d3 100644 (file)
@@ -218,6 +218,11 @@ public abstract class Layout {
         mTextDir = textDir;
     }
 
+    /** @hide */
+    protected void setJustify(boolean justify) {
+        mJustify = justify;
+    }
+
     /**
      * Replace constructor properties of this Layout with new ones.  Be careful.
      */
@@ -266,6 +271,99 @@ public abstract class Layout {
         drawText(canvas, firstLine, lastLine);
     }
 
+    private boolean isJustificationRequired(int lineNum) {
+        if (!mJustify) return false;
+        final int lineEnd = getLineEnd(lineNum);
+        return lineEnd < mText.length() && mText.charAt(lineEnd - 1) != '\n';
+    }
+
+    private float getJustifyWidth(int lineNum) {
+        Alignment paraAlign = mAlignment;
+        TabStops tabStops = null;
+        boolean tabStopsIsInitialized = false;
+
+        int left = 0;
+        int right = mWidth;
+
+        final int dir = getParagraphDirection(lineNum);
+
+        ParagraphStyle[] spans = NO_PARA_SPANS;
+        if (mSpannedText) {
+            Spanned sp = (Spanned) mText;
+            final int start = getLineStart(lineNum);
+
+            final boolean isFirstParaLine = (start == 0 || mText.charAt(start - 1) == '\n');
+
+            if (isFirstParaLine) {
+                final int spanEnd = sp.nextSpanTransition(start, mText.length(),
+                        ParagraphStyle.class);
+                spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
+
+                for (int n = spans.length - 1; n >= 0; n--) {
+                    if (spans[n] instanceof AlignmentSpan) {
+                        paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
+                        break;
+                    }
+                }
+            }
+
+            final int length = spans.length;
+            boolean useFirstLineMargin = isFirstParaLine;
+            for (int n = 0; n < length; n++) {
+                if (spans[n] instanceof LeadingMarginSpan2) {
+                    int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
+                    int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
+                    if (lineNum < startLine + count) {
+                        useFirstLineMargin = true;
+                        break;
+                    }
+                }
+            }
+            for (int n = 0; n < length; n++) {
+                if (spans[n] instanceof LeadingMarginSpan) {
+                    LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
+                    if (dir == DIR_RIGHT_TO_LEFT) {
+                        right -= margin.getLeadingMargin(useFirstLineMargin);
+                    } else {
+                        left += margin.getLeadingMargin(useFirstLineMargin);
+                    }
+                }
+            }
+        }
+
+        if (getLineContainsTab(lineNum)) {
+            tabStops = new TabStops(TAB_INCREMENT, spans);
+        }
+
+        final Alignment align;
+        if (paraAlign == Alignment.ALIGN_LEFT) {
+            align = (dir == DIR_LEFT_TO_RIGHT) ?  Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
+        } else if (paraAlign == Alignment.ALIGN_RIGHT) {
+            align = (dir == DIR_LEFT_TO_RIGHT) ?  Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
+        } else {
+            align = paraAlign;
+        }
+
+        final int indentWidth;
+        if (align == Alignment.ALIGN_NORMAL) {
+            if (dir == DIR_LEFT_TO_RIGHT) {
+                indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+            } else {
+                indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+            }
+        } else if (align == Alignment.ALIGN_OPPOSITE) {
+            if (dir == DIR_LEFT_TO_RIGHT) {
+                indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+            } else {
+                indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+            }
+        } else { // Alignment.ALIGN_CENTER
+            indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
+        }
+
+        return right - left - indentWidth;
+    }
+
     /**
      * @hide
      */
@@ -274,7 +372,7 @@ public abstract class Layout {
         int previousLineEnd = getLineStart(firstLine);
         ParagraphStyle[] spans = NO_PARA_SPANS;
         int spanEnd = 0;
-        TextPaint paint = mPaint;
+        final TextPaint paint = mPaint;
         CharSequence buf = mText;
 
         Alignment paraAlign = mAlignment;
@@ -288,6 +386,7 @@ public abstract class Layout {
         for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
             int start = previousLineEnd;
             previousLineEnd = getLineStart(lineNum + 1);
+            final boolean justify = isJustificationRequired(lineNum);
             int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
 
             int ltop = previousLineBottom;
@@ -386,34 +485,42 @@ public abstract class Layout {
             }
 
             int x;
+            final int indentWidth;
             if (align == Alignment.ALIGN_NORMAL) {
                 if (dir == DIR_LEFT_TO_RIGHT) {
-                    x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                    indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                    x = left + indentWidth;
                 } else {
-                    x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                    indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                    x = right - indentWidth;
                 }
             } else {
                 int max = (int)getLineExtent(lineNum, tabStops, false);
                 if (align == Alignment.ALIGN_OPPOSITE) {
                     if (dir == DIR_LEFT_TO_RIGHT) {
-                        x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                        indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+                        x = right - max - indentWidth;
                     } else {
-                        x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                        indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+                        x = left - max + indentWidth;
                     }
                 } else { // Alignment.ALIGN_CENTER
+                    indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
                     max = max & ~1;
-                    x = ((right + left - max) >> 1) +
-                            getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
+                    x = ((right + left - max) >> 1) + indentWidth;
                 }
             }
 
             paint.setHyphenEdit(getHyphen(lineNum));
             Directions directions = getLineDirections(lineNum);
-            if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) {
+            if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab && !justify) {
                 // XXX: assumes there's nothing additional to be done
                 canvas.drawText(buf, start, end, x, lbaseline, paint);
             } else {
                 tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
+                if (justify) {
+                    tl.justify(right - left - indentWidth);
+                }
                 tl.draw(canvas, x, ltop, lbaseline, lbottom);
             }
             paint.setHyphenEdit(0);
@@ -1094,6 +1201,9 @@ public abstract class Layout {
         TextLine tl = TextLine.obtain();
         mPaint.setHyphenEdit(getHyphen(line));
         tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
+        if (isJustificationRequired(line)) {
+            tl.justify(getJustifyWidth(line));
+        }
         float width = tl.metrics(null);
         mPaint.setHyphenEdit(0);
         TextLine.recycle(tl);
@@ -1118,6 +1228,9 @@ public abstract class Layout {
         TextLine tl = TextLine.obtain();
         mPaint.setHyphenEdit(getHyphen(line));
         tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
+        if (isJustificationRequired(line)) {
+            tl.justify(getJustifyWidth(line));
+        }
         float width = tl.metrics(null);
         mPaint.setHyphenEdit(0);
         TextLine.recycle(tl);
@@ -1303,10 +1416,7 @@ public abstract class Layout {
                 return end - 1;
             }
 
-            // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
-            if (!(ch == ' ' || ch == '\t' || ch == 0x1680 ||
-                    (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) ||
-                    ch == 0x205F || ch == 0x3000)) {
+            if (!TextLine.isLineEndSpace(ch)) {
                 break;
             }
 
@@ -2086,6 +2196,7 @@ public abstract class Layout {
     private boolean mSpannedText;
     private TextDirectionHeuristic mTextDir;
     private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
+    private boolean mJustify;
 
     public static final int DIR_LEFT_TO_RIGHT = 1;
     public static final int DIR_RIGHT_TO_LEFT = -1;
index 081be3a..cb5b073 100644 (file)
@@ -94,6 +94,7 @@ public class StaticLayout extends Layout {
             b.mMaxLines = Integer.MAX_VALUE;
             b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
             b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
+            b.mJustify = false;
 
             b.mMeasuredText = MeasuredText.obtain();
             return b;
@@ -320,6 +321,17 @@ public class StaticLayout extends Layout {
         }
 
         /**
+         * Enables or disables paragraph justification. The default value is disabled (false).
+         *
+         * @param justify true for enabling and false for disabling paragraph justification.
+         * @return this builder, useful for chaining.
+         */
+        public Builder setJustify(boolean justify) {
+            mJustify = justify;
+            return this;
+        }
+
+        /**
          * Measurement and break iteration is done in native code. The protocol for using
          * the native code is as follows.
          *
@@ -404,6 +416,7 @@ public class StaticLayout extends Layout {
         int mHyphenationFrequency;
         int[] mLeftIndents;
         int[] mRightIndents;
+        boolean mJustify;
 
         Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
@@ -557,6 +570,7 @@ public class StaticLayout extends Layout {
 
         mLeftIndents = b.mLeftIndents;
         mRightIndents = b.mRightIndents;
+        setJustify(b.mJustify);
 
         generate(b, b.mIncludePad, b.mIncludePad);
     }
@@ -676,7 +690,8 @@ public class StaticLayout extends Layout {
 
             nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
                     firstWidth, firstWidthLineCount, restWidth,
-                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
+                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency,
+                    b.mJustify);
             if (mLeftIndents != null || mRightIndents != null) {
                 // TODO(raph) performance: it would be better to do this once per layout rather
                 // than once per paragraph, but that would require a change to the native
@@ -1284,7 +1299,8 @@ public class StaticLayout extends Layout {
     // Set up paragraph text and settings; done as one big method to minimize jni crossings
     private static native void nSetupParagraph(long nativePtr, char[] text, int length,
             float firstWidth, int firstWidthLineCount, float restWidth,
-            int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency);
+            int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency,
+            boolean isJustified);
 
     private static native float nAddStyleRun(long nativePtr, long nativePaint,
             long nativeTypeface, int start, int end, boolean isRtl);
index c411860..fcff9a2 100644 (file)
@@ -54,6 +54,10 @@ class TextLine {
     private char[] mChars;
     private boolean mCharsValid;
     private Spanned mSpanned;
+
+    // Additional width of whitespace for justification. This value is per whitespace, thus
+    // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
+    private float mAddedWidth;
     private final TextPaint mWorkPaint = new TextPaint();
     private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
             new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
@@ -177,6 +181,25 @@ class TextLine {
             }
         }
         mTabs = tabStops;
+        mAddedWidth = 0;
+    }
+
+    /**
+     * Justify the line to the given width.
+     */
+    void justify(float justifyWidth) {
+        int end = mLen;
+        while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) {
+            end--;
+        }
+        final int spaces = countStretchableSpaces(0, end);
+        if (spaces == 0) {
+            // There are no stretchable spaces, so we can't help the justification by adding any
+            // width.
+            return;
+        }
+        final float width = Math.abs(measure(end, false, null));
+        mAddedWidth = (justifyWidth - width) / spaces;
     }
 
     /**
@@ -595,6 +618,7 @@ class TextLine {
 
         TextPaint wp = mWorkPaint;
         wp.set(mPaint);
+        wp.setWordSpacing(mAddedWidth);
 
         int spanStart = runStart;
         int spanLimit;
@@ -695,6 +719,7 @@ class TextLine {
             Canvas c, float x, int top, int y, int bottom,
             FontMetricsInt fmi, boolean needWidth, int offset) {
 
+        wp.setWordSpacing(mAddedWidth);
         // Get metrics first (even for empty strings or "0" width runs)
         if (fmi != null) {
             expandMetricsFromPaint(fmi, wp);
@@ -981,5 +1006,34 @@ class TextLine {
         return TabStops.nextDefaultStop(h, TAB_INCREMENT);
     }
 
+    private boolean isStretchableWhitespace(int ch) {
+        // TODO: Support other stretchable whitespace. (Bug: 34013491)
+        return ch == 0x0020 || ch == 0x00A0;
+    }
+
+    private int nextStretchableSpace(int start, int end) {
+        for (int i = start; i < end; i++) {
+            final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart);
+            if (isStretchableWhitespace(c)) return i;
+        }
+        return end;
+    }
+
+    /* Return the number of spaces in the text line, for the purpose of justification */
+    private int countStretchableSpaces(int start, int end) {
+        int count = 0;
+        for (int i = start; i < end; i = nextStretchableSpace(i + 1, end)) {
+            count++;
+        }
+        return count;
+    }
+
+    // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
+    public static boolean isLineEndSpace(char ch) {
+        return ch == ' ' || ch == '\t' || ch == 0x1680
+                || (0x2000 <= ch && ch <= 0x200A && ch != 0x2007)
+                || ch == 0x205F || ch == 0x3000;
+    }
+
     private static final int TAB_INCREMENT = 20;
 }
index 1961bf6..70034e4 100644 (file)
@@ -596,6 +596,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
     private int mBreakStrategy;
     private int mHyphenationFrequency;
+    private boolean mJustify;
 
     private int mMaximum = Integer.MAX_VALUE;
     private int mMaxMode = LINES;
@@ -769,6 +770,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         String fontFeatureSettings = null;
         mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
         mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
+        mJustify = false;
 
         final Resources.Theme theme = context.getTheme();
 
@@ -3298,6 +3300,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     }
 
     /**
+     * Enables or disables full justification. The default value is false.
+     *
+     * @see #getJustify()
+     */
+    public void setJustify(boolean justify) {
+        mJustify = justify;
+        if (mLayout != null) {
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    /**
+     * @return true if currently paragraph justification is enabled.
+     *
+     * @see #setJustify(boolean)
+     */
+    public boolean getJustify() {
+        return mJustify;
+    }
+
+    /**
      * Sets font feature settings. The format is the same as the CSS
      * font-feature-settings attribute:
      * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
@@ -7170,6 +7195,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         .setIncludePad(mIncludePad)
                         .setBreakStrategy(mBreakStrategy)
                         .setHyphenationFrequency(mHyphenationFrequency)
+                        .setJustify(mJustify)
                         .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
                 if (shouldEllipsize) {
                     builder.setEllipsize(mEllipsize)
@@ -7211,7 +7237,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         if (mText instanceof Spannable) {
             result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth,
                     alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad,
-                    mBreakStrategy, mHyphenationFrequency,
+                    mBreakStrategy, mHyphenationFrequency, mJustify,
                     getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth);
         } else {
             if (boring == UNKNOWN_BORING) {
@@ -7261,6 +7287,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     .setIncludePad(mIncludePad)
                     .setBreakStrategy(mBreakStrategy)
                     .setHyphenationFrequency(mHyphenationFrequency)
+                    .setJustify(mJustify)
                     .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
             if (shouldEllipsize) {
                 builder.setEllipsize(effectiveEllipsize)
index 02fa872..c05ef26 100644 (file)
@@ -54,7 +54,8 @@ static JLineBreaksID gLineBreaks_fieldID;
 // hyphenFrequency)
 static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
         jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
-        jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency) {
+        jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency,
+        jboolean isJustified) {
     minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
     b->resize(length);
     env->GetCharArrayRegion(text, 0, length, b->buffer());
@@ -68,6 +69,7 @@ static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray tex
     }
     b->setStrategy(static_cast<minikin::BreakStrategy>(strategy));
     b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency));
+    b->setJustified(isJustified);
 }
 
 static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
@@ -190,7 +192,7 @@ static const JNINativeMethod gMethods[] = {
     {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
     {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator},
     {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
-    {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph},
+    {"nSetupParagraph", "(J[CIFIF[IIIIZ)V", (void*) nSetupParagraph},
     {"nSetIndents", "(J[I)V", (void*) nSetIndents},
     {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
     {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},