blob: e3f0e0a453414259bbd47207b7ab8af74e10be17 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.text.cts;
import android.test.AndroidTestCase;
import android.text.Editable;
import android.text.GetChars;
import android.text.GraphicsOperations;
import android.text.Layout.Alignment;
import android.text.TextUtils.TruncateAt;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.SpannedString;
import android.text.StaticLayout;
import android.text.TextDirectionHeuristics;
import android.text.TextPaint;
import android.text.TextUtils;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.List;
public class StaticLayoutTest extends AndroidTestCase {
private static final float SPACE_MULTI = 1.0f;
private static final float SPACE_ADD = 0.0f;
private static final int DEFAULT_OUTER_WIDTH = 150;
private static final int LAST_LINE = 5;
private static final int LINE_COUNT = 6;
private static final int LARGER_THAN_LINE_COUNT = 50;
/* the first line must have one tab. the others not. totally 6 lines
*/
private static final CharSequence LAYOUT_TEXT = "CharSe\tq\nChar"
+ "Sequence\nCharSequence\nHelllo\n, world\nLongLongLong";
private static final CharSequence LAYOUT_TEXT_SINGLE_LINE = "CharSequence";
private static final int VERTICAL_BELOW_TEXT = 1000;
private static final Alignment DEFAULT_ALIGN = Alignment.ALIGN_CENTER;
private static final int ELLIPSIZE_WIDTH = 8;
private StaticLayout mDefaultLayout;
private TextPaint mDefaultPaint;
@Override
protected void setUp() throws Exception {
super.setUp();
if (mDefaultPaint == null) {
mDefaultPaint = new TextPaint();
}
if (mDefaultLayout == null) {
mDefaultLayout = createDefaultStaticLayout();
}
}
private StaticLayout createDefaultStaticLayout() {
return new StaticLayout(LAYOUT_TEXT, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
}
private StaticLayout createEllipsizeStaticLayout() {
return new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(), mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true,
TextUtils.TruncateAt.MIDDLE, ELLIPSIZE_WIDTH);
}
private StaticLayout createEllipsizeStaticLayout(CharSequence text,
TextUtils.TruncateAt ellipsize, int maxLines) {
return new StaticLayout(text, 0, text.length(),
mDefaultPaint, DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN,
TextDirectionHeuristics.FIRSTSTRONG_LTR,
SPACE_MULTI, SPACE_ADD, true /* include pad */,
ellipsize,
ELLIPSIZE_WIDTH,
maxLines);
}
/**
* Constructor test
*/
public void testConstructor() {
new StaticLayout(LAYOUT_TEXT, mDefaultPaint, DEFAULT_OUTER_WIDTH,
DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(), mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(), mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, false, null, 0);
try {
new StaticLayout(null, null, -1, null, 0, 0, true);
fail("should throw NullPointerException here");
} catch (NullPointerException e) {
}
}
public void testBuilder() {
{
// Obtain.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
StaticLayout layout = builder.build();
// Check values passed to obtain().
assertEquals(LAYOUT_TEXT, layout.getText());
assertEquals(mDefaultPaint, layout.getPaint());
assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
// Check default values.
assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
layout.getTextDirectionHeuristic());
assertEquals(Alignment.ALIGN_NORMAL, layout.getAlignment());
assertEquals(0.0f, layout.getSpacingAdd());
assertEquals(1.0f, layout.getSpacingMultiplier());
assertEquals(DEFAULT_OUTER_WIDTH, layout.getEllipsizedWidth());
}
{
// Obtain with null objects.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(null, 0, 0, null, 0);
try {
StaticLayout layout = builder.build();
fail("should throw NullPointerException here");
} catch (NullPointerException e) {
}
}
{
// setText.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setText(LAYOUT_TEXT_SINGLE_LINE);
StaticLayout layout = builder.build();
assertEquals(LAYOUT_TEXT_SINGLE_LINE, layout.getText());
}
{
// setAlignment.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setAlignment(DEFAULT_ALIGN);
StaticLayout layout = builder.build();
assertEquals(DEFAULT_ALIGN, layout.getAlignment());
}
{
// setTextDirection.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setTextDirection(TextDirectionHeuristics.RTL);
StaticLayout layout = builder.build();
// Always returns TextDirectionHeuristics.FIRSTSTRONG_LTR.
assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
layout.getTextDirectionHeuristic());
}
{
// setLineSpacing.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setLineSpacing(1.0f, 2.0f);
StaticLayout layout = builder.build();
assertEquals(1.0f, layout.getSpacingAdd());
assertEquals(2.0f, layout.getSpacingMultiplier());
}
{
// setEllipsizedWidth and setEllipsize.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setEllipsize(TruncateAt.END);
builder.setEllipsizedWidth(ELLIPSIZE_WIDTH);
StaticLayout layout = builder.build();
assertEquals(ELLIPSIZE_WIDTH, layout.getEllipsizedWidth());
assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
assertTrue(layout.getEllipsisCount(0) == 0);
assertTrue(layout.getEllipsisCount(5) > 0);
}
{
// setMaxLines.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setMaxLines(1);
builder.setEllipsize(TruncateAt.END);
StaticLayout layout = builder.build();
assertTrue(layout.getEllipsisCount(0) > 0);
assertEquals(1, layout.getLineCount());
}
{
// Setter methods that cannot be directly tested.
// setBreakStrategy, setHyphenationFrequency, setIncludePad, and setIndents.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY);
builder.setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_FULL);
builder.setIncludePad(true);
builder.setIndents(null, null);
StaticLayout layout = builder.build();
assertNotNull(layout);
}
}
/*
* Get the line number corresponding to the specified vertical position.
* If you ask for a position above 0, you get 0. above 0 means pixel above the fire line
* if you ask for a position in the range of the height, return the pixel in line
* if you ask for a position below the bottom of the text, you get the last line.
* Test 4 values containing -1, 0, normal number and > count
*/
public void testGetLineForVertical() {
assertEquals(0, mDefaultLayout.getLineForVertical(-1));
assertEquals(0, mDefaultLayout.getLineForVertical(0));
assertTrue(mDefaultLayout.getLineForVertical(50) > 0);
assertEquals(LAST_LINE, mDefaultLayout.getLineForVertical(VERTICAL_BELOW_TEXT));
}
/**
* Return the number of lines of text in this layout.
*/
public void testGetLineCount() {
assertEquals(LINE_COUNT, mDefaultLayout.getLineCount());
}
/*
* Return the vertical position of the top of the specified line.
* If the specified line is one beyond the last line, returns the bottom of the last line.
* A line of text contains top and bottom in height. this method just get the top of a line
* Test 4 values containing -1, 0, normal number and > count
*/
public void testGetLineTop() {
assertTrue(mDefaultLayout.getLineTop(0) >= 0);
assertTrue(mDefaultLayout.getLineTop(1) > mDefaultLayout.getLineTop(0));
try {
mDefaultLayout.getLineTop(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getLineTop(LARGER_THAN_LINE_COUNT );
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/**
* Return the descent of the specified line.
* This method just like getLineTop, descent means the bottom pixel of the line
* Test 4 values containing -1, 0, normal number and > count
*/
public void testGetLineDescent() {
assertTrue(mDefaultLayout.getLineDescent(0) > 0);
assertTrue(mDefaultLayout.getLineDescent(1) > 0);
try {
mDefaultLayout.getLineDescent(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getLineDescent(LARGER_THAN_LINE_COUNT );
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/**
* Returns the primary directionality of the paragraph containing the specified line.
* By default, each line should be same
*/
public void testGetParagraphDirection() {
assertEquals(mDefaultLayout.getParagraphDirection(0),
mDefaultLayout.getParagraphDirection(1));
try {
mDefaultLayout.getParagraphDirection(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getParagraphDirection(LARGER_THAN_LINE_COUNT);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/**
* Return the text offset of the beginning of the specified line.
* If the specified line is one beyond the last line, returns the end of the last line.
* Test 4 values containing -1, 0, normal number and > count
* Each line's offset must >= 0
*/
public void testGetLineStart() {
assertTrue(mDefaultLayout.getLineStart(0) >= 0);
assertTrue(mDefaultLayout.getLineStart(1) >= 0);
try {
mDefaultLayout.getLineStart(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getLineStart(LARGER_THAN_LINE_COUNT);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/*
* Returns whether the specified line contains one or more tabs.
*/
public void testGetContainsTab() {
assertTrue(mDefaultLayout.getLineContainsTab(0));
assertFalse(mDefaultLayout.getLineContainsTab(1));
try {
mDefaultLayout.getLineContainsTab(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getLineContainsTab(LARGER_THAN_LINE_COUNT );
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/**
* Returns an array of directionalities for the specified line.
* The array alternates counts of characters in left-to-right
* and right-to-left segments of the line.
* We can not check the return value, for Directions's field is package private
* So only check it not null
*/
public void testGetLineDirections() {
assertNotNull(mDefaultLayout.getLineDirections(0));
assertNotNull(mDefaultLayout.getLineDirections(1));
try {
mDefaultLayout.getLineDirections(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getLineDirections(LARGER_THAN_LINE_COUNT);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/**
* Returns the (negative) number of extra pixels of ascent padding
* in the top line of the Layout.
*/
public void testGetTopPadding() {
assertTrue(mDefaultLayout.getTopPadding() < 0);
}
/**
* Returns the number of extra pixels of descent padding in the bottom line of the Layout.
*/
public void testGetBottomPadding() {
assertTrue(mDefaultLayout.getBottomPadding() > 0);
}
/*
* Returns the number of characters to be ellipsized away, or 0 if no ellipsis is to take place.
* So each line must >= 0
*/
public void testGetEllipsisCount() {
// Multilines (6 lines) and TruncateAt.START so no ellipsis at all
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
TextUtils.TruncateAt.MIDDLE,
Integer.MAX_VALUE /* maxLines */);
assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(5) == 0);
try {
mDefaultLayout.getEllipsisCount(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getEllipsisCount(LARGER_THAN_LINE_COUNT);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
// Multilines (6 lines) and TruncateAt.MIDDLE so no ellipsis at all
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
TextUtils.TruncateAt.MIDDLE,
Integer.MAX_VALUE /* maxLines */);
assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(5) == 0);
// Multilines (6 lines) and TruncateAt.END so ellipsis only on the last line
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
TextUtils.TruncateAt.END,
Integer.MAX_VALUE /* maxLines */);
assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(5) > 0);
// Multilines (6 lines) and TruncateAt.MARQUEE so ellipsis only on the last line
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT,
TextUtils.TruncateAt.END,
Integer.MAX_VALUE /* maxLines */);
assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(1) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(2) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(3) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(4) == 0);
assertTrue(mDefaultLayout.getEllipsisCount(5) > 0);
// Single line case and TruncateAt.END so that we have some ellipsis
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
TextUtils.TruncateAt.END,
1);
assertTrue(mDefaultLayout.getEllipsisCount(0) > 0);
// Single line case and TruncateAt.MIDDLE so that we have some ellipsis
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
TextUtils.TruncateAt.MIDDLE,
1);
assertTrue(mDefaultLayout.getEllipsisCount(0) > 0);
// Single line case and TruncateAt.END so that we have some ellipsis
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
TextUtils.TruncateAt.END,
1);
assertTrue(mDefaultLayout.getEllipsisCount(0) > 0);
// Single line case and TruncateAt.MARQUEE so that we have NO ellipsis
mDefaultLayout = createEllipsizeStaticLayout(LAYOUT_TEXT_SINGLE_LINE,
TextUtils.TruncateAt.MARQUEE,
1);
assertTrue(mDefaultLayout.getEllipsisCount(0) == 0);
}
/*
* Return the offset of the first character to be ellipsized away
* relative to the start of the line.
* (So 0 if the beginning of the line is ellipsized, not getLineStart().)
*/
public void testGetEllipsisStart() {
mDefaultLayout = createEllipsizeStaticLayout();
assertTrue(mDefaultLayout.getEllipsisStart(0) >= 0);
assertTrue(mDefaultLayout.getEllipsisStart(1) >= 0);
try {
mDefaultLayout.getEllipsisStart(-1);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
mDefaultLayout.getEllipsisStart(LARGER_THAN_LINE_COUNT);
fail("should throw ArrayIndexOutOfBoundsException");
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/*
* Return the width to which this Layout is ellipsizing
* or getWidth() if it is not doing anything special.
* The constructor's Argument TextUtils.TruncateAt defines which EllipsizedWidth to use
* ellipsizedWidth if argument is not null
* outerWidth if argument is null
*/
public void testGetEllipsizedWidth() {
int ellipsizedWidth = 60;
int outerWidth = 100;
StaticLayout layout = new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(),
mDefaultPaint, outerWidth, DEFAULT_ALIGN, SPACE_MULTI,
SPACE_ADD, false, TextUtils.TruncateAt.END, ellipsizedWidth);
assertEquals(ellipsizedWidth, layout.getEllipsizedWidth());
layout = new StaticLayout(LAYOUT_TEXT, 0, LAYOUT_TEXT.length(),
mDefaultPaint, outerWidth, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD,
false, null, ellipsizedWidth);
assertEquals(outerWidth, layout.getEllipsizedWidth());
}
/**
* scenario description:
* 1. set the text.
* 2. change the text
* 3. Check the text won't change to the StaticLayout
*/
public void testImmutableStaticLayout() {
Editable editable = Editable.Factory.getInstance().newEditable("123\t\n555");
StaticLayout layout = new StaticLayout(editable, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
assertEquals(2, layout.getLineCount());
assertTrue(mDefaultLayout.getLineContainsTab(0));
// change the text
editable.delete(0, editable.length() - 1);
assertEquals(2, layout.getLineCount());
assertTrue(layout.getLineContainsTab(0));
}
// String wrapper for testing not well known implementation of CharSequence.
private class FakeCharSequence implements CharSequence {
private String mStr;
public FakeCharSequence(String str) {
mStr = str;
}
@Override
public char charAt(int index) {
return mStr.charAt(index);
}
@Override
public int length() {
return mStr.length();
}
@Override
public CharSequence subSequence(int start, int end) {
return mStr.subSequence(start, end);
}
@Override
public String toString() {
return mStr;
}
};
private List<CharSequence> buildTestCharSequences(String testString, Normalizer.Form[] forms) {
List<CharSequence> result = new ArrayList<CharSequence>();
List<String> normalizedStrings = new ArrayList<String>();
for (Normalizer.Form form: forms) {
normalizedStrings.add(Normalizer.normalize(testString, form));
}
for (String str: normalizedStrings) {
result.add(str);
result.add(new SpannedString(str));
result.add(new SpannableString(str));
result.add(new SpannableStringBuilder(str)); // as a GraphicsOperations implementation.
result.add(new FakeCharSequence(str)); // as a not well known implementation.
}
return result;
}
private String buildTestMessage(CharSequence seq) {
String normalized;
if (Normalizer.isNormalized(seq, Normalizer.Form.NFC)) {
normalized = "NFC";
} else if (Normalizer.isNormalized(seq, Normalizer.Form.NFD)) {
normalized = "NFD";
} else if (Normalizer.isNormalized(seq, Normalizer.Form.NFKC)) {
normalized = "NFKC";
} else if (Normalizer.isNormalized(seq, Normalizer.Form.NFKD)) {
normalized = "NFKD";
} else {
throw new IllegalStateException("Normalized form is not NFC/NFD/NFKC/NFKD");
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < seq.length(); ++i) {
builder.append(String.format("0x%04X ", Integer.valueOf(seq.charAt(i))));
}
return "testString: \"" + seq.toString() + "\"[" + builder.toString() + "]" +
", class: " + seq.getClass().getName() +
", Normalization: " + normalized;
}
public void testGetOffset_ASCII() {
String testStrings[] = { "abcde", "ab\ncd", "ab\tcd", "ab\n\nc", "ab\n\tc" };
for (String testString: testStrings) {
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(5));
}
}
String testString = "ab\r\nde";
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 5, layout.getOffsetToLeftOf(6));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(6));
}
}
public void testGetOffset_UNICODE() {
String testStrings[] = new String[] {
// Cyrillic alphabets.
"\u0410\u0411\u0412\u0413\u0414",
// Japanese Hiragana Characters.
"\u3042\u3044\u3046\u3048\u304A",
};
for (String testString: testStrings) {
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(5));
}
}
}
public void testGetOffset_UNICODE_Normalization() {
// "A" with acute, circumflex, tilde, diaeresis, ring above.
String testString = "\u00C1\u00C2\u00C3\u00C4\u00C5";
Normalizer.Form[] oneUnicodeForms = { Normalizer.Form.NFC, Normalizer.Form.NFKC };
for (CharSequence seq: buildTestCharSequences(testString, oneUnicodeForms)) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(5));
}
Normalizer.Form[] twoUnicodeForms = { Normalizer.Form.NFD, Normalizer.Form.NFKD };
for (CharSequence seq: buildTestCharSequences(testString, twoUnicodeForms)) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(6));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(7));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(8));
assertEquals(testLabel, 8, layout.getOffsetToLeftOf(9));
assertEquals(testLabel, 8, layout.getOffsetToLeftOf(10));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
assertEquals(testLabel, 8, layout.getOffsetToRightOf(6));
assertEquals(testLabel, 8, layout.getOffsetToRightOf(7));
assertEquals(testLabel, 10, layout.getOffsetToRightOf(8));
assertEquals(testLabel, 10, layout.getOffsetToRightOf(9));
assertEquals(testLabel, 10, layout.getOffsetToRightOf(10));
}
}
public void testGetOffset_UNICODE_SurrogatePairs() {
// Emoticons for surrogate pairs tests.
String testString =
"\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03\uD83D\uDE04";
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(6));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(7));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(8));
assertEquals(testLabel, 8, layout.getOffsetToLeftOf(9));
assertEquals(testLabel, 8, layout.getOffsetToLeftOf(10));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
assertEquals(testLabel, 8, layout.getOffsetToRightOf(6));
assertEquals(testLabel, 8, layout.getOffsetToRightOf(7));
assertEquals(testLabel, 10, layout.getOffsetToRightOf(8));
assertEquals(testLabel, 10, layout.getOffsetToRightOf(9));
assertEquals(testLabel, 10, layout.getOffsetToRightOf(10));
}
}
public void testGetOffset_UNICODE_Thai() {
// Thai Characters. The expected cursorable boundary is
// | \u0E02 | \u0E2D | \u0E1A | \u0E04\u0E38 | \u0E13 |
String testString = "\u0E02\u0E2D\u0E1A\u0E04\u0E38\u0E13";
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 5, layout.getOffsetToLeftOf(6));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 3, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(6));
}
}
public void testGetOffset_UNICODE_Hebrew() {
String testString = "\u05DE\u05E1\u05E2\u05D3\u05D4"; // Hebrew Characters
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN,
TextDirectionHeuristics.RTL, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 5, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 5, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 0, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 0, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 3, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(5));
}
}
public void testGetOffset_UNICODE_Arabic() {
// Arabic Characters. The expected cursorable boundary is
// | \u0623 \u064F | \u0633 \u0652 | \u0631 \u064E | \u0629 \u064C |";
String testString = "\u0623\u064F\u0633\u0652\u0631\u064E\u0629\u064C";
Normalizer.Form[] oneUnicodeForms = { Normalizer.Form.NFC, Normalizer.Form.NFKC };
for (CharSequence seq: buildTestCharSequences(testString, oneUnicodeForms)) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 8, layout.getOffsetToLeftOf(6));
assertEquals(testLabel, 8, layout.getOffsetToLeftOf(7));
assertEquals(testLabel, 8, layout.getOffsetToLeftOf(8));
assertEquals(testLabel, 0, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 0, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 0, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(5));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(6));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(7));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(8));
}
}
public void testGetOffset_UNICODE_Bidi() {
// String having RTL characters and LTR characters
// LTR Context
// The first and last two characters are LTR characters.
String testString = "\u0061\u0062\u05DE\u05E1\u05E2\u0063\u0064";
// Logical order: [L1] [L2] [R1] [R2] [R3] [L3] [L4]
// 0 1 2 3 4 5 6 7
// Display order: [L1] [L2] [R3] [R2] [R1] [L3] [L4]
// 0 1 2 4 3 5 6 7
// [L?] means ?th LTR character and [R?] means ?th RTL character.
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 0, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 5, layout.getOffsetToLeftOf(6));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(7));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 3, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(5));
assertEquals(testLabel, 7, layout.getOffsetToRightOf(6));
assertEquals(testLabel, 7, layout.getOffsetToRightOf(7));
}
// RTL Context
// The first and last two characters are RTL characters.
String testString2 = "\u05DE\u05E1\u0063\u0064\u0065\u05DE\u05E1";
// Logical order: [R1] [R2] [L1] [L2] [L3] [R3] [R4]
// 0 1 2 3 4 5 6 7
// Display order: [R4] [R3] [L1] [L2] [L3] [R2] [R1]
// 7 6 5 3 4 2 1 0
// [L?] means ?th LTR character and [R?] means ?th RTL character.
for (CharSequence seq: buildTestCharSequences(testString2, Normalizer.Form.values())) {
StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
String testLabel = buildTestMessage(seq);
assertEquals(testLabel, 1, layout.getOffsetToLeftOf(0));
assertEquals(testLabel, 2, layout.getOffsetToLeftOf(1));
assertEquals(testLabel, 4, layout.getOffsetToLeftOf(2));
assertEquals(testLabel, 5, layout.getOffsetToLeftOf(3));
assertEquals(testLabel, 3, layout.getOffsetToLeftOf(4));
assertEquals(testLabel, 6, layout.getOffsetToLeftOf(5));
assertEquals(testLabel, 7, layout.getOffsetToLeftOf(6));
assertEquals(testLabel, 7, layout.getOffsetToLeftOf(7));
assertEquals(testLabel, 0, layout.getOffsetToRightOf(0));
assertEquals(testLabel, 0, layout.getOffsetToRightOf(1));
assertEquals(testLabel, 1, layout.getOffsetToRightOf(2));
assertEquals(testLabel, 4, layout.getOffsetToRightOf(3));
assertEquals(testLabel, 2, layout.getOffsetToRightOf(4));
assertEquals(testLabel, 3, layout.getOffsetToRightOf(5));
assertEquals(testLabel, 5, layout.getOffsetToRightOf(6));
assertEquals(testLabel, 6, layout.getOffsetToRightOf(7));
}
}
}