Merge "Store line extra in layouts"
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index c7a5fce..91d8290 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -365,6 +365,7 @@
desc += botpad;
ints[DESCENT] = desc;
+ ints[EXTRA] = reflowed.getLineExtra(i);
objects[0] = reflowed.getLineDirections(i);
final int end = (i == n - 1) ? where + after : reflowed.getLineStart(i + 1);
@@ -692,6 +693,14 @@
return mInts.getValue(line, DESCENT);
}
+ /**
+ * @hide
+ */
+ @Override
+ public int getLineExtra(int line) {
+ return mInts.getValue(line, EXTRA);
+ }
+
@Override
public int getLineStart(int line) {
return mInts.getValue(line, START) & START_MASK;
@@ -851,14 +860,15 @@
private static final int TAB = START;
private static final int TOP = 1;
private static final int DESCENT = 2;
+ private static final int EXTRA = 3;
// HYPHEN and MAY_PROTRUDE_FROM_TOP_OR_BOTTOM share the same entry.
- private static final int HYPHEN = 3;
+ private static final int HYPHEN = 4;
private static final int MAY_PROTRUDE_FROM_TOP_OR_BOTTOM = HYPHEN;
- private static final int COLUMNS_NORMAL = 4;
+ private static final int COLUMNS_NORMAL = 5;
- private static final int ELLIPSIS_START = 4;
- private static final int ELLIPSIS_COUNT = 5;
- private static final int COLUMNS_ELLIPSIZE = 6;
+ private static final int ELLIPSIS_START = 5;
+ private static final int ELLIPSIS_COUNT = 6;
+ private static final int COLUMNS_ELLIPSIZE = 7;
private static final int START_MASK = 0x1FFFFFFF;
private static final int DIR_SHIFT = 30;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 0f910cc..2dc3f60 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -17,6 +17,7 @@
package android.text;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
@@ -1466,6 +1467,17 @@
return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line));
}
+ /**
+ * Return the extra space added as a result of line spacing attributes
+ * {@link #getSpacingAdd()} and {@link #getSpacingMultiplier()}. Default value is {@code zero}.
+ *
+ * @param line the index of the line, the value should be equal or greater than {@code zero}
+ * @hide
+ */
+ public int getLineExtra(@IntRange(from = 0) int line) {
+ return 0;
+ }
+
public int getOffsetToLeftOf(int offset) {
return getOffsetToLeftRightOf(offset, true);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 54f9ea1..dc5553e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1006,6 +1006,7 @@
lines[off + START] = start;
lines[off + TOP] = v;
lines[off + DESCENT] = below + extra;
+ lines[off + EXTRA] = extra;
// special case for non-ellipsized last visible line when maxLines is set
// store the height as if it was ellipsized
@@ -1194,6 +1195,14 @@
return mLines[mColumns * line + TOP];
}
+ /**
+ * @hide
+ */
+ @Override
+ public int getLineExtra(int line) {
+ return mLines[mColumns * line + EXTRA];
+ }
+
@Override
public int getLineDescent(int line) {
return mLines[mColumns * line + DESCENT];
@@ -1216,6 +1225,9 @@
@Override
public final Directions getLineDirections(int line) {
+ if (line > getLineCount()) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
return mLineDirections[line];
}
@@ -1367,16 +1379,17 @@
*/
private int mMaxLineHeight = -1;
- private static final int COLUMNS_NORMAL = 4;
- private static final int COLUMNS_ELLIPSIZE = 6;
+ private static final int COLUMNS_NORMAL = 5;
+ private static final int COLUMNS_ELLIPSIZE = 7;
private static final int START = 0;
private static final int DIR = START;
private static final int TAB = START;
private static final int TOP = 1;
private static final int DESCENT = 2;
- private static final int HYPHEN = 3;
- private static final int ELLIPSIS_START = 4;
- private static final int ELLIPSIS_COUNT = 5;
+ private static final int EXTRA = 3;
+ private static final int HYPHEN = 4;
+ private static final int ELLIPSIS_START = 5;
+ private static final int ELLIPSIS_COUNT = 6;
private int[] mLines;
private Directions[] mLineDirections;
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
index 811bf2c..5ef08e0 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
@@ -18,6 +18,7 @@
import static android.text.Layout.Alignment.ALIGN_NORMAL;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -112,4 +113,77 @@
assertFalse(layout.getBlocksAlwaysNeedToBeRedrawn().contains(0));
assertTrue(layout.getBlocksAlwaysNeedToBeRedrawn().isEmpty());
}
+
+ @Test
+ public void testGetLineExtra_withoutLinespacing() {
+ final SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc");
+ final TextPaint textPaint = new TextPaint();
+
+ // create a StaticLayout to check against
+ final StaticLayout staticLayout = StaticLayout.Builder.obtain(text, 0,
+ text.length(), textPaint, WIDTH)
+ .setAlignment(ALIGN_NORMAL)
+ .setIncludePad(false)
+ .build();
+
+ // create the DynamicLayout
+ final DynamicLayout dynamicLayout = new DynamicLayout(text,
+ textPaint,
+ WIDTH,
+ ALIGN_NORMAL,
+ 1f /*spacingMultiplier*/,
+ 0 /*spacingAdd*/,
+ false /*includepad*/);
+
+ final int lineCount = staticLayout.getLineCount();
+ assertEquals(lineCount, dynamicLayout.getLineCount());
+ for (int i = 0; i < lineCount; i++) {
+ assertEquals(staticLayout.getLineExtra(i), dynamicLayout.getLineExtra(i));
+ }
+ }
+
+ @Test
+ public void testGetLineExtra_withLinespacing() {
+ final SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc");
+ final TextPaint textPaint = new TextPaint();
+ final float spacingMultiplier = 2f;
+ final float spacingAdd = 4;
+
+ // create a StaticLayout to check against
+ final StaticLayout staticLayout = StaticLayout.Builder.obtain(text, 0,
+ text.length(), textPaint, WIDTH)
+ .setAlignment(ALIGN_NORMAL)
+ .setIncludePad(false)
+ .setLineSpacing(spacingAdd, spacingMultiplier)
+ .build();
+
+ // create the DynamicLayout
+ final DynamicLayout dynamicLayout = new DynamicLayout(text,
+ textPaint,
+ WIDTH,
+ ALIGN_NORMAL,
+ spacingMultiplier,
+ spacingAdd,
+ false /*includepad*/);
+
+ final int lineCount = staticLayout.getLineCount();
+ assertEquals(lineCount, dynamicLayout.getLineCount());
+ for (int i = 0; i < lineCount - 1; i++) {
+ assertEquals(staticLayout.getLineExtra(i), dynamicLayout.getLineExtra(i));
+ }
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testGetLineExtra_withNegativeValue() {
+ final DynamicLayout layout = new DynamicLayout("", new TextPaint(), 10 /*width*/,
+ ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/);
+ layout.getLineExtra(-1);
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testGetLineExtra_withParamGreaterThanLineCount() {
+ final DynamicLayout layout = new DynamicLayout("", new TextPaint(), 10 /*width*/,
+ ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/);
+ layout.getLineExtra(100);
+ }
}
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 6d610bb..6b262eb 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -215,6 +215,17 @@
}
@Test
+ public void testGetLineExtra_returnsZeroByDefault() {
+ final String text = "a\nb\nc\n";
+ final Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, 100 /* spacingMult*/, 100 /*spacingAdd*/);
+ final int lineCount = text.split("\n").length;
+ for (int i = 0; i < lineCount; i++) {
+ assertEquals(0, layout.getLineExtra(i));
+ }
+ }
+
+ @Test
public void testGetLineVisibleEnd() {
Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
mAlign, mSpacingMult, mSpacingAdd);
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index fb60e38..ad16e89 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -107,7 +107,7 @@
Layout l = b.build();
assertVertMetrics(l, 0, 0,
- fmi.ascent, fmi.descent);
+ new int[][]{{fmi.ascent, fmi.descent, 0}});
// other quick metrics
assertEquals(0, l.getLineStart(0));
@@ -124,14 +124,14 @@
* Top and bottom padding are affected, as is the line descent and height.
*/
@Test
- public void testGetters2() {
+ public void testLineMetrics_withPadding() {
LayoutBuilder b = builder()
.setIncludePad(true);
FontMetricsInt fmi = b.paint.getFontMetricsInt();
Layout l = b.build();
assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
- fmi.top, fmi.bottom);
+ new int[][]{{fmi.top, fmi.bottom, 0}});
}
/**
@@ -139,16 +139,18 @@
* Ascent of top line and descent of bottom line are affected.
*/
@Test
- public void testGetters3() {
+ public void testLineMetrics_withPaddingAndWidth() {
LayoutBuilder b = builder()
.setIncludePad(true)
.setWidth(50);
FontMetricsInt fmi = b.paint.getFontMetricsInt();
- Layout l = b.build();
+ Layout l = b.build();
assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
- fmi.top, fmi.descent,
- fmi.ascent, fmi.bottom);
+ new int[][]{
+ {fmi.top, fmi.descent, 0},
+ {fmi.ascent, fmi.bottom, 0}
+ });
}
/**
@@ -156,7 +158,7 @@
* First line ascent is top, bottom line descent is bottom.
*/
@Test
- public void testGetters4() {
+ public void testLineMetrics_withThreeLines() {
LayoutBuilder b = builder()
.setText("This is a longer test")
.setIncludePad(true)
@@ -165,9 +167,11 @@
Layout l = b.build();
assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
- fmi.top, fmi.descent,
- fmi.ascent, fmi.descent,
- fmi.ascent, fmi.bottom);
+ new int[][]{
+ {fmi.top, fmi.descent, 0},
+ {fmi.ascent, fmi.descent, 0},
+ {fmi.ascent, fmi.bottom, 0}
+ });
}
/**
@@ -176,7 +180,7 @@
* even be non-zero leading.
*/
@Test
- public void testGetters5() {
+ public void testLineMetrics_withLargeText() {
LayoutBuilder b = builder()
.setText("This is a longer test")
.setIncludePad(true)
@@ -193,9 +197,11 @@
// using leading, this will fail.
Layout l = b.build();
assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
- fmi.top, fmi.descent,
- fmi.ascent, fmi.descent,
- fmi.ascent, fmi.bottom);
+ new int[][]{
+ {fmi.top, fmi.descent, 0},
+ {fmi.ascent, fmi.descent, 0},
+ {fmi.ascent, fmi.bottom, 0}
+ });
}
/**
@@ -203,7 +209,7 @@
* to 3 lines.
*/
@Test
- public void testGetters6() {
+ public void testLineMetrics_withSpacingAdd() {
int spacingAdd = 2; // int so expressions return int
LayoutBuilder b = builder()
.setText("This is a longer test")
@@ -214,9 +220,11 @@
Layout l = b.build();
assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
- fmi.top, fmi.descent + spacingAdd,
- fmi.ascent, fmi.descent + spacingAdd,
- fmi.ascent, fmi.bottom);
+ new int[][]{
+ {fmi.top, fmi.descent + spacingAdd, spacingAdd},
+ {fmi.ascent, fmi.descent + spacingAdd, spacingAdd},
+ {fmi.ascent, fmi.bottom, 0}
+ });
}
/**
@@ -224,7 +232,7 @@
* spacingMult = 1.5, wrapping to 3 lines.
*/
@Test
- public void testGetters7() {
+ public void testLineMetrics_withSpacingMult() {
LayoutBuilder b = builder()
.setText("This is a longer test")
.setIncludePad(true)
@@ -236,9 +244,13 @@
Layout l = b.build();
assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
- fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
- fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
- fmi.ascent, fmi.bottom);
+ new int[][]{
+ {fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
+ s.scale(fmi.descent - fmi.top)},
+ {fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
+ s.scale(fmi.descent - fmi.ascent)},
+ {fmi.ascent, fmi.bottom, 0}
+ });
}
/**
@@ -246,7 +258,7 @@
* spacingMult = 0.8 when wrapping to 3 lines.
*/
@Test
- public void testGetters8() {
+ public void testLineMetrics_withUnitIntervalSpacingMult() {
LayoutBuilder b = builder()
.setText("This is a longer test")
.setIncludePad(true)
@@ -258,9 +270,25 @@
Layout l = b.build();
assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
- fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
- fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
- fmi.ascent, fmi.bottom);
+ new int[][]{
+ {fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
+ s.scale(fmi.descent - fmi.top)},
+ {fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
+ s.scale(fmi.descent - fmi.ascent)},
+ {fmi.ascent, fmi.bottom, 0}
+ });
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testGetLineExtra_withNegativeValue() {
+ final Layout layout = builder().build();
+ layout.getLineExtra(-1);
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testGetLineExtra_withParamGreaterThanLineCount() {
+ final Layout layout = builder().build();
+ layout.getLineExtra(100);
}
// ----- test utility classes and methods -----
@@ -341,26 +369,39 @@
}
}
- private void assertVertMetrics(Layout l, int topPad, int botPad, int... values) {
+ /**
+ * Assert vertical metrics such as top, bottom, ascent, descent.
+ * @param l layout instance
+ * @param topPad top padding
+ * @param botPad bottom padding
+ * @param values values for each line where first is ascent, second is descent, and last one is
+ * extra
+ */
+ private void assertVertMetrics(Layout l, int topPad, int botPad, int[][] values) {
assertTopBotPadding(l, topPad, botPad);
assertLinesMetrics(l, values);
}
- private void assertLinesMetrics(Layout l, int... values) {
- // sanity check
- if ((values.length & 0x1) != 0) {
- throw new IllegalArgumentException(String.valueOf(values.length));
- }
-
- int lines = values.length >> 1;
+ /**
+ * Check given expected values against the Layout values.
+ * @param l layout instance
+ * @param values values for each line where first is ascent, second is descent, and last one is
+ * extra
+ */
+ private void assertLinesMetrics(Layout l, int[][] values) {
+ final int lines = values.length;
assertEquals(lines, l.getLineCount());
int t = 0;
- for (int i = 0, n = 0; i < lines; ++i, n += 2) {
- int a = values[n];
- int d = values[n+1];
+ for (int i = 0, n = 0; i < lines; ++i, n += 3) {
+ if (values[i].length != 3) {
+ throw new IllegalArgumentException(String.valueOf(values.length));
+ }
+ int a = values[i][0];
+ int d = values[i][1];
+ int extra = values[i][2];
int h = -a + d;
- assertLineMetrics(l, i, t, a, d, h);
+ assertLineMetrics(l, i, t, a, d, h, extra);
t += h;
}
@@ -368,12 +409,13 @@
}
private void assertLineMetrics(Layout l, int line,
- int top, int ascent, int descent, int height) {
+ int top, int ascent, int descent, int height, int extra) {
String info = "line " + line;
assertEquals(info, top, l.getLineTop(line));
assertEquals(info, ascent, l.getLineAscent(line));
assertEquals(info, descent, l.getLineDescent(line));
assertEquals(info, height, l.getLineBottom(line) - top);
+ assertEquals(info, extra, l.getLineExtra(line));
}
private void assertTopBotPadding(Layout l, int topPad, int botPad) {