summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Felt <dougfelt@google.com>2011-07-07 11:57:48 -0700
committerDoug Felt <dougfelt@google.com>2011-07-14 11:24:33 -0700
commitcb379120456d8065d742021fc5c66748fc8a11a8 (patch)
tree980cb4378ded06d096f8606073a2f32245f6df87
parentad3f935ce9f3308edc62d56a0059e0761c720077 (diff)
downloadframeworks_base-cb379120456d8065d742021fc5c66748fc8a11a8.zip
frameworks_base-cb379120456d8065d742021fc5c66748fc8a11a8.tar.gz
frameworks_base-cb379120456d8065d742021fc5c66748fc8a11a8.tar.bz2
Implement textDirection heuristic selection.
Change-Id: I2fcf18de573f2d66494fa5ed61e4273c3c6078c7
-rw-r--r--core/java/android/text/BoringLayout.java28
-rw-r--r--core/java/android/text/DynamicLayout.java31
-rw-r--r--core/java/android/text/Layout.java39
-rw-r--r--core/java/android/text/MeasuredText.java20
-rw-r--r--core/java/android/text/StaticLayout.java53
-rw-r--r--core/java/android/text/TextDirectionHeuristic.java13
-rw-r--r--core/java/android/text/TextDirectionHeuristics.java310
-rw-r--r--core/java/android/text/TextUtils.java55
-rw-r--r--core/java/android/view/View.java75
-rw-r--r--core/java/android/view/ViewGroup.java32
-rw-r--r--core/java/android/widget/TextView.java210
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java10
-rw-r--r--tests/BiDiTests/res/layout/textview_direction_ltr.xml305
-rw-r--r--tests/BiDiTests/res/layout/textview_direction_rtl.xml305
-rw-r--r--tests/BiDiTests/res/values/strings.xml2
15 files changed, 1053 insertions, 435 deletions
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 757a8c3..5a244f1 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -226,7 +226,17 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
*/
public static Metrics isBoring(CharSequence text,
TextPaint paint) {
- return isBoring(text, paint, null);
+ return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null);
+ }
+
+ /**
+ * Returns null if not boring; the width, ascent, and descent if boring.
+ * @hide
+ */
+ public static Metrics isBoring(CharSequence text,
+ TextPaint paint,
+ TextDirectionHeuristic textDir) {
+ return isBoring(text, paint, textDir, null);
}
/**
@@ -235,6 +245,17 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
* if boring.
*/
public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
+ return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics);
+ }
+
+ /**
+ * Returns null if not boring; the width, ascent, and descent in the
+ * provided Metrics object (or a new one if the provided one was null)
+ * if boring.
+ * @hide
+ */
+ public static Metrics isBoring(CharSequence text, TextPaint paint,
+ TextDirectionHeuristic textDir, Metrics metrics) {
char[] temp = TextUtils.obtain(500);
int length = text.length();
boolean boring = true;
@@ -258,6 +279,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
break outer;
}
}
+
+ if (textDir.isRtl(temp, 0, n)) {
+ boring = false;
+ break outer;
+ }
}
TextUtils.recycle(temp);
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index f196b34..cb96969 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -75,12 +75,31 @@ extends Layout
float spacingmult, float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
- super((ellipsize == null)
- ? display
- : (display instanceof Spanned)
- ? new SpannedEllipsizer(display)
+ this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth);
+ }
+
+ /**
+ * Make a layout for the transformed text (password transformation
+ * being the primary example of a transformation)
+ * that will be updated as the base text is changed.
+ * If ellipsize is non-null, the Layout will ellipsize the text
+ * down to ellipsizedWidth.
+ * *
+ * *@hide
+ */
+ public DynamicLayout(CharSequence base, CharSequence display,
+ TextPaint paint,
+ int width, Alignment align, TextDirectionHeuristic textDir,
+ float spacingmult, float spacingadd,
+ boolean includepad,
+ TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+ super((ellipsize == null)
+ ? display
+ : (display instanceof Spanned)
+ ? new SpannedEllipsizer(display)
: new Ellipsizer(display),
- paint, width, align, spacingmult, spacingadd);
+ paint, width, align, textDir, spacingmult, spacingadd);
mBase = base;
mDisplay = display;
@@ -259,7 +278,7 @@ extends Layout
reflowed = new StaticLayout(true);
reflowed.generate(text, where, where + after,
- getPaint(), getWidth(), getAlignment(),
+ getPaint(), getWidth(), getAlignment(), getTextDirectionHeuristic(),
getSpacingMultiplier(), getSpacingAdd(),
false, true, mEllipsizedWidth, mEllipsizeAt);
int n = reflowed.getLineCount();
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index aae9ccf..eabeef0 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,8 +16,6 @@
package android.text;
-import com.android.internal.util.ArrayUtils;
-
import android.emoji.EmojiFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -32,6 +30,8 @@ import android.text.style.ParagraphStyle;
import android.text.style.ReplacementSpan;
import android.text.style.TabStopSpan;
+import com.android.internal.util.ArrayUtils;
+
import java.util.Arrays;
/**
@@ -113,6 +113,29 @@ public abstract class Layout {
protected Layout(CharSequence text, TextPaint paint,
int width, Alignment align,
float spacingMult, float spacingAdd) {
+ this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ spacingMult, spacingAdd);
+ }
+
+ /**
+ * Subclasses of Layout use this constructor to set the display text,
+ * width, and other standard properties.
+ * @param text the text to render
+ * @param paint the default paint for the layout. Styles can override
+ * various attributes of the paint.
+ * @param width the wrapping width for the text.
+ * @param align whether to left, right, or center the text. Styles can
+ * override the alignment.
+ * @param spacingMult factor by which to scale the font size to get the
+ * default line spacing
+ * @param spacingAdd amount to add to the default line spacing
+ *
+ * @hide
+ */
+ protected Layout(CharSequence text, TextPaint paint,
+ int width, Alignment align, TextDirectionHeuristic textDir,
+ float spacingMult, float spacingAdd) {
+
if (width < 0)
throw new IllegalArgumentException("Layout: " + width + " < 0");
@@ -133,6 +156,7 @@ public abstract class Layout {
mSpacingMult = spacingMult;
mSpacingAdd = spacingAdd;
mSpannedText = text instanceof Spanned;
+ mTextDir = textDir;
}
/**
@@ -531,6 +555,14 @@ public abstract class Layout {
}
/**
+ * Return the heuristic used to determine paragraph text direction.
+ * @hide
+ */
+ public final TextDirectionHeuristic getTextDirectionHeuristic() {
+ return mTextDir;
+ }
+
+ /**
* Return the number of lines of text in this layout.
*/
public abstract int getLineCount();
@@ -1419,7 +1451,7 @@ public abstract class Layout {
MeasuredText mt = MeasuredText.obtain();
TextLine tl = TextLine.obtain();
try {
- mt.setPara(text, start, end, DIR_REQUEST_LTR);
+ mt.setPara(text, start, end, TextDirectionHeuristics.LTR);
Directions directions;
int dir;
if (mt.mEasy) {
@@ -1769,6 +1801,7 @@ public abstract class Layout {
private float mSpacingAdd;
private static final Rect sTempRect = new Rect();
private boolean mSpannedText;
+ private TextDirectionHeuristic mTextDir;
public static final int DIR_LEFT_TO_RIGHT = 1;
public static final int DIR_RIGHT_TO_LEFT = -1;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index a81be09..2920ac5 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -85,7 +85,7 @@ class MeasuredText {
* Analyzes text for bidirectional runs. Allocates working buffers.
*/
/* package */
- void setPara(CharSequence text, int start, int end, int bidiRequest) {
+ void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
mText = text;
mTextStart = start;
@@ -115,13 +115,29 @@ class MeasuredText {
}
}
- if (TextUtils.doesNotNeedBidi(mChars, 0, len)) {
+ if ((textDir == TextDirectionHeuristics.LTR ||
+ textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR ||
+ textDir == TextDirectionHeuristics.ANYRTL_LTR) &&
+ TextUtils.doesNotNeedBidi(mChars, 0, len)) {
mDir = Layout.DIR_LEFT_TO_RIGHT;
mEasy = true;
} else {
if (mLevels == null || mLevels.length < len) {
mLevels = new byte[ArrayUtils.idealByteArraySize(len)];
}
+ int bidiRequest;
+ if (textDir == TextDirectionHeuristics.LTR) {
+ bidiRequest = Layout.DIR_REQUEST_LTR;
+ } else if (textDir == TextDirectionHeuristics.RTL) {
+ bidiRequest = Layout.DIR_REQUEST_RTL;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
+ bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
+ bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
+ } else {
+ boolean isRtl = textDir.isRtl(mChars, 0, len);
+ bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
+ }
mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false);
mEasy = false;
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 9e48eff..f7b9502 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,8 +16,6 @@
package android.text;
-import com.android.internal.util.ArrayUtils;
-
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.text.style.LeadingMarginSpan;
@@ -26,6 +24,8 @@ import android.text.style.LineHeightSpan;
import android.text.style.MetricAffectingSpan;
import android.text.style.TabStopSpan;
+import com.android.internal.util.ArrayUtils;
+
/**
* StaticLayout is a Layout for text that will not be edited after it
* is laid out. Use {@link DynamicLayout} for text that may change.
@@ -46,6 +46,17 @@ public class StaticLayout extends Layout {
spacingmult, spacingadd, includepad);
}
+ /**
+ * @hide
+ */
+ public StaticLayout(CharSequence source, TextPaint paint,
+ int width, Alignment align, TextDirectionHeuristic textDir,
+ float spacingmult, float spacingadd,
+ boolean includepad) {
+ this(source, 0, source.length(), paint, width, align, textDir,
+ spacingmult, spacingadd, includepad);
+ }
+
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align,
@@ -55,9 +66,35 @@ public class StaticLayout extends Layout {
spacingmult, spacingadd, includepad, null, 0);
}
+ /**
+ * @hide
+ */
+ public StaticLayout(CharSequence source, int bufstart, int bufend,
+ TextPaint paint, int outerwidth,
+ Alignment align, TextDirectionHeuristic textDir,
+ float spacingmult, float spacingadd,
+ boolean includepad) {
+ this(source, bufstart, bufend, paint, outerwidth, align, textDir,
+ spacingmult, spacingadd, includepad, null, 0);
+}
+
+ public StaticLayout(CharSequence source, int bufstart, int bufend,
+ TextPaint paint, int outerwidth,
+ Alignment align,
+ float spacingmult, float spacingadd,
+ boolean includepad,
+ TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+ this(source, bufstart, bufend, paint, outerwidth, align,
+ TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth);
+ }
+
+ /**
+ * @hide
+ */
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
- Alignment align,
+ Alignment align, TextDirectionHeuristic textDir,
float spacingmult, float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
@@ -66,7 +103,7 @@ public class StaticLayout extends Layout {
: (source instanceof Spanned)
? new SpannedEllipsizer(source)
: new Ellipsizer(source),
- paint, outerwidth, align, spacingmult, spacingadd);
+ paint, outerwidth, align, textDir, spacingmult, spacingadd);
/*
* This is annoying, but we can't refer to the layout until
@@ -96,7 +133,7 @@ public class StaticLayout extends Layout {
mMeasured = MeasuredText.obtain();
- generate(source, bufstart, bufend, paint, outerwidth, align,
+ generate(source, bufstart, bufend, paint, outerwidth, align, textDir,
spacingmult, spacingadd, includepad, includepad,
ellipsizedWidth, ellipsize);
@@ -116,7 +153,7 @@ public class StaticLayout extends Layout {
/* package */ void generate(CharSequence source, int bufStart, int bufEnd,
TextPaint paint, int outerWidth,
- Alignment align,
+ Alignment align, TextDirectionHeuristic textDir,
float spacingmult, float spacingadd,
boolean includepad, boolean trackpad,
float ellipsizedWidth, TextUtils.TruncateAt ellipsize) {
@@ -157,7 +194,7 @@ public class StaticLayout extends Layout {
LeadingMarginSpan lms = sp[i];
firstWidth -= sp[i].getLeadingMargin(true);
restWidth -= sp[i].getLeadingMargin(false);
-
+
// LeadingMarginSpan2 is odd. The count affects all
// leading margin spans, not just this particular one,
// and start from the top of the span, not the top of the
@@ -195,7 +232,7 @@ public class StaticLayout extends Layout {
}
}
- measured.setPara(source, paraStart, paraEnd, DIR_REQUEST_DEFAULT_LTR);
+ measured.setPara(source, paraStart, paraEnd, textDir);
char[] chs = measured.mChars;
float[] widths = measured.mWidths;
byte[] chdirs = measured.mLevels;
diff --git a/core/java/android/text/TextDirectionHeuristic.java b/core/java/android/text/TextDirectionHeuristic.java
new file mode 100644
index 0000000..130f879
--- /dev/null
+++ b/core/java/android/text/TextDirectionHeuristic.java
@@ -0,0 +1,13 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package android.text;
+
+/**
+ * Interface for objects that guess at the paragraph direction by examining text.
+ *
+ * @hide
+ */
+public interface TextDirectionHeuristic {
+ /** @hide */ boolean isRtl(CharSequence text, int start, int end);
+ /** @hide */ boolean isRtl(char[] text, int start, int count);
+}
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
new file mode 100644
index 0000000..5f9ffc5
--- /dev/null
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -0,0 +1,310 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package android.text;
+
+
+/**
+ * Some objects that implement TextDirectionHeuristic.
+ * @hide
+ */
+public class TextDirectionHeuristics {
+
+ /** Always decides that the direction is left to right. */
+ public static final TextDirectionHeuristic LTR =
+ new TextDirectionHeuristicInternal(null /* no algorithm */, false);
+
+ /** Always decides that the direction is right to left. */
+ public static final TextDirectionHeuristic RTL =
+ new TextDirectionHeuristicInternal(null /* no algorithm */, true);
+
+ /**
+ * Determines the direction based on the first strong directional character,
+ * including bidi format chars, falling back to left to right if it
+ * finds none. This is the default behavior of the Unicode Bidirectional
+ * Algorithm.
+ */
+ public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
+ new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
+
+ /**
+ * Determines the direction based on the first strong directional character,
+ * including bidi format chars, falling back to right to left if it
+ * finds none. This is similar to the default behavior of the Unicode
+ * Bidirectional Algorithm, just with different fallback behavior.
+ */
+ public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
+ new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
+
+ /**
+ * If the text contains any strong right to left non-format character, determines
+ * that the direction is right to left, falling back to left to right if it
+ * finds none.
+ */
+ public static final TextDirectionHeuristic ANYRTL_LTR =
+ new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
+
+ /**
+ * If the text contains any strong left to right non-format character, determines
+ * that the direction is left to right, falling back to right to left if it
+ * finds none.
+ */
+ public static final TextDirectionHeuristic ANYLTR_RTL =
+ new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_LTR, true);
+
+ /**
+ * Examines only the strong directional non-format characters, and if either
+ * left to right or right to left characters are 60% or more of this total,
+ * determines that the direction follows the majority of characters. Falls
+ * back to left to right if neither direction meets this threshold.
+ */
+ public static final TextDirectionHeuristic CHARCOUNT_LTR =
+ new TextDirectionHeuristicInternal(CharCount.INSTANCE_DEFAULT, false);
+
+ /**
+ * Examines only the strong directional non-format characters, and if either
+ * left to right or right to left characters are 60% or more of this total,
+ * determines that the direction follows the majority of characters. Falls
+ * back to right to left if neither direction meets this threshold.
+ */
+ public static final TextDirectionHeuristic CHARCOUNT_RTL =
+ new TextDirectionHeuristicInternal(CharCount.INSTANCE_DEFAULT, true);
+
+ private static enum TriState {
+ TRUE, FALSE, UNKNOWN;
+ }
+
+ /**
+ * Computes the text direction based on an algorithm. Subclasses implement
+ * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
+ * direction from the text alone.
+ * @hide
+ */
+ public static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
+ private final TextDirectionAlgorithm mAlgorithm;
+
+ public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
+ mAlgorithm = algorithm;
+ }
+
+ /**
+ * Return true if the default text direction is rtl.
+ */
+ abstract protected boolean defaultIsRtl();
+
+ @Override
+ public boolean isRtl(CharSequence text, int start, int end) {
+ if (text == null || start < 0 || end < start || text.length() < end) {
+ throw new IllegalArgumentException();
+ }
+ if (mAlgorithm == null) {
+ return defaultIsRtl();
+ }
+ text = text.subSequence(start, end);
+ char[] chars = text.toString().toCharArray();
+ return doCheck(chars, 0, chars.length);
+ }
+
+ @Override
+ public boolean isRtl(char[] chars, int start, int count) {
+ if (chars == null || start < 0 || count < 0 || chars.length - count < start) {
+ throw new IllegalArgumentException();
+ }
+ if (mAlgorithm == null) {
+ return defaultIsRtl();
+ }
+ return doCheck(chars, start, count);
+ }
+
+ private boolean doCheck(char[] chars, int start, int count) {
+ switch(mAlgorithm.checkRtl(chars, start, count)) {
+ case TRUE:
+ return true;
+ case FALSE:
+ return false;
+ default:
+ return defaultIsRtl();
+ }
+ }
+ }
+
+ private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl {
+ private final boolean mDefaultIsRtl;
+
+ private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
+ boolean defaultIsRtl) {
+ super(algorithm);
+ mDefaultIsRtl = defaultIsRtl;
+ }
+
+ @Override
+ protected boolean defaultIsRtl() {
+ return mDefaultIsRtl;
+ }
+ }
+
+ private static TriState isRtlText(int directionality) {
+ switch (directionality) {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ return TriState.FALSE;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+ return TriState.TRUE;
+ default:
+ return TriState.UNKNOWN;
+ }
+ }
+
+ private static TriState isRtlTextOrFormat(int directionality) {
+ switch (directionality) {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+ return TriState.FALSE;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+ return TriState.TRUE;
+ default:
+ return TriState.UNKNOWN;
+ }
+ }
+
+ /**
+ * Interface for an algorithm to guess the direction of a paragraph of text.
+ *
+ * @hide
+ */
+ public static interface TextDirectionAlgorithm {
+ /**
+ * Returns whether the range of text is RTL according to the algorithm.
+ *
+ * @hide
+ */
+ TriState checkRtl(char[] text, int start, int count);
+ }
+
+ /**
+ * Algorithm that uses the first strong directional character to determine
+ * the paragraph direction. This is the standard Unicode Bidirectional
+ * algorithm.
+ *
+ * @hide
+ */
+ public static class FirstStrong implements TextDirectionAlgorithm {
+ @Override
+ public TriState checkRtl(char[] text, int start, int count) {
+ TriState result = TriState.UNKNOWN;
+ for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) {
+ result = isRtlTextOrFormat(Character.getDirectionality(text[i]));
+ }
+ return result;
+ }
+
+ private FirstStrong() {
+ }
+
+ public static final FirstStrong INSTANCE = new FirstStrong();
+ }
+
+ /**
+ * Algorithm that uses the presence of any strong directional non-format
+ * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
+ * direction of text.
+ *
+ * @hide
+ */
+ public static class AnyStrong implements TextDirectionAlgorithm {
+ private final boolean mLookForRtl;
+
+ @Override
+ public TriState checkRtl(char[] text, int start, int count) {
+ boolean haveUnlookedFor = false;
+ for (int i = start, e = start + count; i < e; ++i) {
+ switch (isRtlText(Character.getDirectionality(text[i]))) {
+ case TRUE:
+ if (mLookForRtl) {
+ return TriState.TRUE;
+ }
+ haveUnlookedFor = true;
+ break;
+ case FALSE:
+ if (!mLookForRtl) {
+ return TriState.FALSE;
+ }
+ haveUnlookedFor = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (haveUnlookedFor) {
+ return mLookForRtl ? TriState.FALSE : TriState.TRUE;
+ }
+ return TriState.UNKNOWN;
+ }
+
+ private AnyStrong(boolean lookForRtl) {
+ this.mLookForRtl = lookForRtl;
+ }
+
+ public static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
+ public static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
+ }
+
+ /**
+ * Algorithm that uses the relative proportion of strong directional
+ * characters (excluding LRE, LRO, RLE, RLO) to determine the direction
+ * of the paragraph, if the proportion exceeds a given threshold.
+ *
+ * @hide
+ */
+ public static class CharCount implements TextDirectionAlgorithm {
+ private final float mThreshold;
+
+ @Override
+ public TriState checkRtl(char[] text, int start, int count) {
+ int countLtr = 0;
+ int countRtl = 0;
+ for(int i = start, e = start + count; i < e; ++i) {
+ switch (isRtlText(Character.getDirectionality(text[i]))) {
+ case TRUE:
+ ++countLtr;
+ break;
+ case FALSE:
+ ++countRtl;
+ break;
+ default:
+ break;
+ }
+ }
+ int limit = (int)((countLtr + countRtl) * mThreshold);
+ if (limit > 0) {
+ if (countLtr > limit) {
+ return TriState.FALSE;
+ }
+ if (countRtl > limit) {
+ return TriState.TRUE;
+ }
+ }
+ return TriState.UNKNOWN;
+ }
+
+ private CharCount(float threshold) {
+ mThreshold = threshold;
+ }
+
+ public static CharCount withThreshold(float threshold) {
+ if (threshold < 0 || threshold > 1) {
+ throw new IllegalArgumentException();
+ }
+ if (threshold == DEFAULT_THRESHOLD) {
+ return INSTANCE_DEFAULT;
+ }
+ return new CharCount(threshold);
+ }
+
+ public static final float DEFAULT_THRESHOLD = 0.6f;
+ public static final CharCount INSTANCE_DEFAULT = new CharCount(DEFAULT_THRESHOLD);
+ }
+}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 6741059..29c9853 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -16,9 +16,6 @@
package android.text;
-import com.android.internal.R;
-import com.android.internal.util.ArrayUtils;
-
import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
@@ -45,6 +42,9 @@ import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
import android.util.Printer;
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.regex.Pattern;
@@ -1001,13 +1001,37 @@ public class TextUtils {
* will be padded with zero-width spaces to preserve the original
* length and offsets instead of truncating.
* If <code>callback</code> is non-null, it will be called to
- * report the start and end of the ellipsized range.
+ * report the start and end of the ellipsized range. TextDirection
+ * is determined by the first strong directional character.
*/
public static CharSequence ellipsize(CharSequence text,
TextPaint paint,
float avail, TruncateAt where,
boolean preserveLength,
EllipsizeCallback callback) {
+ return ellipsize(text, paint, avail, where, preserveLength, callback,
+ TextDirectionHeuristics.FIRSTSTRONG_LTR);
+ }
+
+ /**
+ * Returns the original text if it fits in the specified width
+ * given the properties of the specified Paint,
+ * or, if it does not fit, a copy with ellipsis character added
+ * at the specified edge or center.
+ * If <code>preserveLength</code> is specified, the returned copy
+ * will be padded with zero-width spaces to preserve the original
+ * length and offsets instead of truncating.
+ * If <code>callback</code> is non-null, it will be called to
+ * report the start and end of the ellipsized range.
+ *
+ * @hide
+ */
+ public static CharSequence ellipsize(CharSequence text,
+ TextPaint paint,
+ float avail, TruncateAt where,
+ boolean preserveLength,
+ EllipsizeCallback callback,
+ TextDirectionHeuristic textDir) {
if (sEllipsis == null) {
Resources r = Resources.getSystem();
sEllipsis = r.getString(R.string.ellipsis);
@@ -1017,8 +1041,7 @@ public class TextUtils {
MeasuredText mt = MeasuredText.obtain();
try {
- float width = setPara(mt, paint, text, 0, text.length(),
- Layout.DIR_REQUEST_DEFAULT_LTR);
+ float width = setPara(mt, paint, text, 0, text.length(), textDir);
if (width <= avail) {
if (callback != null) {
@@ -1108,11 +1131,20 @@ public class TextUtils {
TextPaint p, float avail,
String oneMore,
String more) {
+ return commaEllipsize(text, p, avail, oneMore, more,
+ TextDirectionHeuristics.FIRSTSTRONG_LTR);
+ }
+
+ /**
+ * @hide
+ */
+ public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
+ float avail, String oneMore, String more, TextDirectionHeuristic textDir) {
MeasuredText mt = MeasuredText.obtain();
try {
int len = text.length();
- float width = setPara(mt, p, text, 0, len, Layout.DIR_REQUEST_DEFAULT_LTR);
+ float width = setPara(mt, p, text, 0, len, textDir);
if (width <= avail) {
return text;
}
@@ -1135,9 +1167,6 @@ public class TextUtils {
int count = 0;
float[] widths = mt.mWidths;
- int request = mt.mDir == 1 ? Layout.DIR_REQUEST_LTR :
- Layout.DIR_REQUEST_RTL;
-
MeasuredText tempMt = MeasuredText.obtain();
for (int i = 0; i < len; i++) {
w += widths[i];
@@ -1155,7 +1184,7 @@ public class TextUtils {
}
// XXX this is probably ok, but need to look at it more
- tempMt.setPara(format, 0, format.length(), request);
+ tempMt.setPara(format, 0, format.length(), textDir);
float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null);
if (w + moreWid <= avail) {
@@ -1175,9 +1204,9 @@ public class TextUtils {
}
private static float setPara(MeasuredText mt, TextPaint paint,
- CharSequence text, int start, int end, int bidiRequest) {
+ CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
- mt.setPara(text, start, end, bidiRequest);
+ mt.setPara(text, start, end, textDir);
float width;
Spanned sp = text instanceof Spanned ? (Spanned) text : null;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 74dc100..2f13a85 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,13 +16,6 @@
package android.view;
-import android.util.FloatProperty;
-import android.util.LocaleUtil;
-import android.util.Property;
-import com.android.internal.R;
-import com.android.internal.util.Predicate;
-import com.android.internal.view.menu.MenuBuilder;
-
import android.content.ClipData;
import android.content.Context;
import android.content.res.Configuration;
@@ -53,11 +46,14 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.LocaleUtil;
import android.util.Log;
import android.util.Pool;
import android.util.Poolable;
import android.util.PoolableManager;
import android.util.Pools;
+import android.util.Property;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ContextMenu.ContextMenuInfo;
@@ -72,6 +68,10 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.ScrollBarDrawable;
+import com.android.internal.R;
+import com.android.internal.util.Predicate;
+import com.android.internal.view.menu.MenuBuilder;
+
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -2493,12 +2493,6 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
private boolean mSendingHoverAccessibilityEvents;
/**
- * Undefined text direction (used by resolution algorithm).
- * @hide
- */
- public static final int TEXT_DIRECTION_UNDEFINED = -1;
-
- /**
* Text direction is inherited thru {@link ViewGroup}
* @hide
*/
@@ -2507,7 +2501,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
/**
* Text direction is using "first strong algorithm". The first strong directional character
* determines the paragraph direction. If there is no strong directional character, the
- * paragraph direction is the view’s resolved ayout direction.
+ * paragraph direction is the view's resolved ayout direction.
*
* @hide
*/
@@ -2516,7 +2510,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
/**
* Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains
* any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
- * If there are neither, the paragraph direction is the view’s resolved layout direction.
+ * If there are neither, the paragraph direction is the view's resolved layout direction.
*
* @hide
*/
@@ -2560,7 +2554,6 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "text", mapping = {
- @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
@@ -2568,21 +2561,25 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
@ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL")
})
- protected int mTextDirection = DEFAULT_TEXT_DIRECTION;
+ private int mTextDirection = DEFAULT_TEXT_DIRECTION;
/**
- * The resolved text direction. If resolution has not yet been done or has been reset, it will
- * be equal to {@link #TEXT_DIRECTION_UNDEFINED}. Otherwise it will be either {@link #TEXT_DIRECTION_LTR}
- * or {@link #TEXT_DIRECTION_RTL}.
+ * The resolved text direction. This needs resolution if the value is
+ * TEXT_DIRECTION_INHERIT. The resolution matches mTextDirection if that is
+ * not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds up the parent
+ * chain of the view.
*
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "text", mapping = {
- @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_CHAR_COUNT, to = "CHAR_COUNT"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL")
})
- protected int mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+ private int mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
/**
* Consistency verifier for debugging purposes.
@@ -13048,43 +13045,41 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
*
* @return the resolved text direction. Return one of:
*
+ * {@link #TEXT_DIRECTION_FIRST_STRONG}
+ * {@link #TEXT_DIRECTION_ANY_RTL},
+ * {@link #TEXT_DIRECTION_CHAR_COUNT},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
*
* @hide
*/
public int getResolvedTextDirection() {
- if (!isResolvedTextDirection()) {
+ if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) {
resolveTextDirection();
}
return mResolvedTextDirection;
}
/**
- * Resolve the text direction. Classes that extend View and want to do a specific text direction
- * resolution will need to implement this method and set the mResolvedTextDirection to
- * either TEXT_DIRECTION_LTR if direction is LTR or TEXT_DIRECTION_RTL if
- * direction is RTL.
+ * Resolve the text direction.
*/
protected void resolveTextDirection() {
+ if (mTextDirection != TEXT_DIRECTION_INHERIT) {
+ mResolvedTextDirection = mTextDirection;
+ return;
+ }
+ if (mParent != null && mParent instanceof ViewGroup) {
+ mResolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
+ return;
+ }
+ mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
}
/**
- * Return if the text direction has been resolved or not.
- *
- * @return true, if resolved and false if not resolved
- *
- * @hide
- */
- public boolean isResolvedTextDirection() {
- return (mResolvedTextDirection != TEXT_DIRECTION_UNDEFINED);
- }
-
- /**
- * Reset resolved text direction. Will be resolved during a call to getResolvedLayoutDirection().
+ * Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection().
*/
protected void resetResolvedTextDirection() {
- mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+ mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
}
//
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 752fd5a..cb3e9c6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -41,6 +41,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.view.animation.Transformation;
+
import com.android.internal.R;
import com.android.internal.util.Predicate;
@@ -5012,37 +5013,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
- /**
- * This method will be called during text direction resolution (text direction resolution
- * inheritance)
- */
- @Override
- protected void resolveTextDirection() {
- int resolvedTextDirection;
- switch(mTextDirection) {
- default:
- case TEXT_DIRECTION_INHERIT:
- // Try to the text direction from the parent layout
- if (mParent != null && mParent instanceof ViewGroup) {
- resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
- } else {
- // We reached the top of the View hierarchy, so set the text direction
- // heuristic to "first strong"
- resolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
- }
- break;
- // Pass down the hierarchy the following text direction values
- case TEXT_DIRECTION_FIRST_STRONG:
- case TEXT_DIRECTION_ANY_RTL:
- case TEXT_DIRECTION_CHAR_COUNT:
- case TEXT_DIRECTION_LTR:
- case TEXT_DIRECTION_RTL:
- resolvedTextDirection = mTextDirection;
- break;
- }
- mResolvedTextDirection = resolvedTextDirection;
- }
-
@Override
protected void resetResolvedTextDirection() {
super.resetResolvedTextDirection();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 766b520..07703d3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,11 +16,6 @@
package android.widget;
-import com.android.internal.util.FastMath;
-import com.android.internal.widget.EditableInputConnection;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.R;
import android.content.ClipData;
import android.content.ClipData.Item;
@@ -64,6 +59,13 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.StaticLayout;
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextDirectionHeuristics.AnyStrong;
+import android.text.TextDirectionHeuristics.CharCount;
+import android.text.TextDirectionHeuristics.FirstStrong;
+import android.text.TextDirectionHeuristics.TextDirectionAlgorithm;
+import android.text.TextDirectionHeuristics.TextDirectionHeuristicImpl;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
@@ -132,6 +134,11 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.util.FastMath;
+import com.android.internal.widget.EditableInputConnection;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.BreakIterator;
@@ -5660,14 +5667,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Layout.Alignment alignment = getLayoutAlignment();
boolean shouldEllipsize = mEllipsize != null && mInput == null;
+ if (mTextDir == null) {
+ resolveTextDirection();
+ }
if (mText instanceof Spannable) {
mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w,
- alignment, mSpacingMult,
+ alignment, mTextDir, mSpacingMult,
mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null,
ellipsisWidth);
} else {
if (boring == UNKNOWN_BORING) {
- boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring);
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -5704,23 +5714,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else if (shouldEllipsize) {
mLayout = new StaticLayout(mTransformed,
0, mTransformed.length(),
- mTextPaint, w, alignment, mSpacingMult,
+ mTextPaint, w, alignment, mTextDir, mSpacingMult,
mSpacingAdd, mIncludePad, mEllipsize,
ellipsisWidth);
} else {
mLayout = new StaticLayout(mTransformed, mTextPaint,
- w, alignment, mSpacingMult, mSpacingAdd,
+ w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
mIncludePad);
}
} else if (shouldEllipsize) {
mLayout = new StaticLayout(mTransformed,
0, mTransformed.length(),
- mTextPaint, w, alignment, mSpacingMult,
+ mTextPaint, w, alignment, mTextDir, mSpacingMult,
mSpacingAdd, mIncludePad, mEllipsize,
ellipsisWidth);
} else {
mLayout = new StaticLayout(mTransformed, mTextPaint,
- w, alignment, mSpacingMult, mSpacingAdd,
+ w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
mIncludePad);
}
}
@@ -5732,7 +5742,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (shouldEllipsize) hintWidth = w;
if (hintBoring == UNKNOWN_BORING) {
- hintBoring = BoringLayout.isBoring(mHint, mTextPaint,
+ hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
mHintBoring);
if (hintBoring != null) {
mHintBoring = hintBoring;
@@ -5770,23 +5780,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else if (shouldEllipsize) {
mHintLayout = new StaticLayout(mHint,
0, mHint.length(),
- mTextPaint, hintWidth, alignment, mSpacingMult,
+ mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult,
mSpacingAdd, mIncludePad, mEllipsize,
ellipsisWidth);
} else {
mHintLayout = new StaticLayout(mHint, mTextPaint,
- hintWidth, alignment, mSpacingMult, mSpacingAdd,
+ hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
mIncludePad);
}
} else if (shouldEllipsize) {
mHintLayout = new StaticLayout(mHint,
0, mHint.length(),
- mTextPaint, hintWidth, alignment, mSpacingMult,
+ mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult,
mSpacingAdd, mIncludePad, mEllipsize,
ellipsisWidth);
} else {
mHintLayout = new StaticLayout(mHint, mTextPaint,
- hintWidth, alignment, mSpacingMult, mSpacingAdd,
+ hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
mIncludePad);
}
}
@@ -5887,6 +5897,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
BoringLayout.Metrics boring = UNKNOWN_BORING;
BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
+ if (mTextDir == null) {
+ resolveTextDirection();
+ }
+
int des = -1;
boolean fromexisting = false;
@@ -5899,7 +5913,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (des < 0) {
- boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring);
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -10119,11 +10133,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mInBatchEditControllers;
}
+ private class TextViewDirectionHeuristic extends TextDirectionHeuristicImpl {
+ private TextViewDirectionHeuristic(TextDirectionAlgorithm algorithm) {
+ super(algorithm);
+ }
+ @Override
+ protected boolean defaultIsRtl() {
+ return getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ }
+ }
+
/**
* Resolve the text direction.
*
* Text direction of paragraphs in a TextView is determined using a heuristic. If the correct
- * text direction cannot be determined by the heuristic, the view’s resolved layout direction
+ * text direction cannot be determined by the heuristic, the view's resolved layout direction
* determines the direction.
*
* This heuristic and result is applied individually to each paragraph in a TextView, based on
@@ -10132,157 +10156,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
@Override
protected void resolveTextDirection() {
- int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
- switch(mTextDirection) {
+ super.resolveTextDirection();
+
+ int textDir = getResolvedTextDirection();
+ switch (textDir) {
default:
- case TEXT_DIRECTION_INHERIT:
- // Try to the text direction from the parent layout. If not possible, then we will
- // use the default layout direction to decide later
- if (mParent != null && mParent instanceof ViewGroup) {
- resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
- }
- break;
case TEXT_DIRECTION_FIRST_STRONG:
- resolvedTextDirection = getTextDirectionFromFirstStrong(mText);
+ mTextDir = new TextViewDirectionHeuristic(FirstStrong.INSTANCE);
break;
case TEXT_DIRECTION_ANY_RTL:
- resolvedTextDirection = getTextDirectionFromAnyRtl(mText);
+ mTextDir = new TextViewDirectionHeuristic(AnyStrong.INSTANCE_RTL);
break;
case TEXT_DIRECTION_CHAR_COUNT:
- resolvedTextDirection = getTextDirectionFromCharCount(mText);
+ mTextDir = new TextViewDirectionHeuristic(CharCount.INSTANCE_DEFAULT);
break;
case TEXT_DIRECTION_LTR:
- resolvedTextDirection = TEXT_DIRECTION_LTR;
+ mTextDir = TextDirectionHeuristics.LTR;
break;
case TEXT_DIRECTION_RTL:
- resolvedTextDirection = TEXT_DIRECTION_RTL;
+ mTextDir = TextDirectionHeuristics.RTL;
break;
}
- // if we have been so far unable to get the text direction from the heuristics, then we are
- // falling back using the layout direction
- if (resolvedTextDirection == TEXT_DIRECTION_UNDEFINED) {
- switch(getResolvedLayoutDirection()) {
- default:
- case LAYOUT_DIRECTION_LTR:
- resolvedTextDirection = TEXT_DIRECTION_LTR;
- break;
- case LAYOUT_DIRECTION_RTL:
- resolvedTextDirection = TEXT_DIRECTION_RTL;
- break;
- }
- }
- mResolvedTextDirection = resolvedTextDirection;
- }
-
- /**
- * Get text direction following the "first strong" heuristic.
- *
- * @param cs the CharSequence used to get the text direction.
- *
- * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
- * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
- */
- private static int getTextDirectionFromFirstStrong(final CharSequence cs) {
- final int length = cs.length();
- if (length == 0) {
- return TEXT_DIRECTION_UNDEFINED;
- }
- for(int i = 0; i < length; i++) {
- final char c = cs.charAt(i);
- final byte dir = Character.getDirectionality(c);
- if (isStrongLtrChar(dir)) {
- return TEXT_DIRECTION_LTR;
- } else if (isStrongRtlChar(dir)) {
- return TEXT_DIRECTION_RTL;
- }
- }
- return TEXT_DIRECTION_UNDEFINED;
- }
-
- /**
- * Get text direction following the "any RTL" heuristic.
- *
- * @param cs the CharSequence used to get the text direction.
- *
- * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
- * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
- */
- private static int getTextDirectionFromAnyRtl(final CharSequence cs) {
- final int length = cs.length();
- if (length == 0) {
- return TEXT_DIRECTION_UNDEFINED;
- }
- boolean foundStrongLtr = false;
- boolean foundStrongRtl = false;
- for(int i = 0; i < length; i++) {
- final char c = cs.charAt(i);
- final byte dir = Character.getDirectionality(c);
- if (isStrongLtrChar(dir)) {
- foundStrongLtr = true;
- } else if (isStrongRtlChar(dir)) {
- foundStrongRtl = true;
- break;
- }
- }
- if (foundStrongRtl) {
- return TEXT_DIRECTION_RTL;
- }
- if (foundStrongLtr) {
- return TEXT_DIRECTION_LTR;
- }
- return TEXT_DIRECTION_UNDEFINED;
- }
-
- /**
- * Get text direction following the "char count" heuristic.
- *
- * @param cs the CharSequence used to get the text direction.
- *
- * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
- * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
- */
- private int getTextDirectionFromCharCount(CharSequence cs) {
- final int length = cs.length();
- if (length == 0) {
- return TEXT_DIRECTION_UNDEFINED;
- }
- int countLtr = 0;
- int countRtl = 0;
- for(int i = 0; i < length; i++) {
- final char c = cs.charAt(i);
- final byte dir = Character.getDirectionality(c);
- if (isStrongLtrChar(dir)) {
- countLtr++;
- } else if (isStrongRtlChar(dir)) {
- countRtl++;
- }
- }
- final float percentLtr = ((float) countLtr) / (countLtr + countRtl);
- if (percentLtr > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) {
- return TEXT_DIRECTION_LTR;
- }
- final float percentRtl = ((float) countRtl) / (countLtr + countRtl);
- if (percentRtl > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) {
- return TEXT_DIRECTION_RTL;
- }
- return TEXT_DIRECTION_UNDEFINED;
- }
-
- /**
- * Return true if the char direction is corresponding to a "strong RTL char" following the
- * Unicode Bidirectional Algorithm (UBA).
- */
- private static boolean isStrongRtlChar(final byte dir) {
- return (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
- dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC);
- }
-
- /**
- * Return true if the char direction is corresponding to a "strong LTR char" following the
- * Unicode Bidirectional Algorithm (UBA).
- */
- private static boolean isStrongLtrChar(final byte dir) {
- return (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT);
}
@ViewDebug.ExportedProperty(category = "text")
@@ -10386,6 +10280,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private BoringLayout mSavedLayout, mSavedHintLayout;
+ private TextDirectionHeuristic mTextDir = null;
+
private static final InputFilter[] NO_FILTERS = new InputFilter[0];
private InputFilter[] mFilters = NO_FILTERS;
private static final Spanned EMPTY_SPANNED = new SpannedString("");
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index c54e4a1..7dc95db 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -16,13 +16,13 @@
package android.widget;
-import com.android.frameworks.coretests.R;
-
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.GetChars;
import android.view.View;
+import com.android.frameworks.coretests.R;
+
/**
* TextViewTest tests {@link TextView}.
*/
@@ -240,12 +240,12 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewTestA
getActivity().runOnUiThread(new Runnable() {
public void run() {
- tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+ ll.setTextDirection(View.TEXT_DIRECTION_RTL);
+ tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
- assertEquals(true, tv.isResolvedTextDirection());
ll.removeView(tv);
- assertEquals(false, tv.isResolvedTextDirection());
+ assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
}
});
}
diff --git a/tests/BiDiTests/res/layout/textview_direction_ltr.xml b/tests/BiDiTests/res/layout/textview_direction_ltr.xml
index f7b7b8e..2c790ec 100644
--- a/tests/BiDiTests/res/layout/textview_direction_ltr.xml
+++ b/tests/BiDiTests/res/layout/textview_direction_ltr.xml
@@ -18,95 +18,232 @@
android:id="@+id/textview_direction_ltr"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layoutDirection="ltr">
+ android:layoutDirection="ltr"
+ android:textDirection="ltr">
- <LinearLayout android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textDirection="ltr">
-
- <LinearLayout android:orientation="vertical"
+ <TableLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="inherit"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="firstStrong"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="anyRtl"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="ltr"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="rtl"
- />
- </LinearLayout>
+ <TableRow>
+ <TextView android:text="(unspecified)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
- <LinearLayout android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ <TableRow>
+ <TextView android:text="inherit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="inherit"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="firstStrong"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="anyRtl"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="ltr"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="rtl"
- />
- </LinearLayout>
+ <TableRow>
+ <TextView android:text="firstStrong"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="firstStrong"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="firstStrong"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="firstStrong"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="anyRtl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="anyRtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="anyRtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="anyRtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="ltr"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="ltr"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="ltr"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="ltr"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="rtl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="rtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="rtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="rtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
- </LinearLayout>
+ </TableLayout>
-</FrameLayout> \ No newline at end of file
+</FrameLayout>
diff --git a/tests/BiDiTests/res/layout/textview_direction_rtl.xml b/tests/BiDiTests/res/layout/textview_direction_rtl.xml
index 81c5411..1df100d 100644
--- a/tests/BiDiTests/res/layout/textview_direction_rtl.xml
+++ b/tests/BiDiTests/res/layout/textview_direction_rtl.xml
@@ -18,95 +18,232 @@
android:id="@+id/textview_direction_rtl"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layoutDirection="rtl">
+ android:layoutDirection="rtl"
+ android:textDirection="rtl">
- <LinearLayout android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textDirection="rtl">
-
- <LinearLayout android:orientation="vertical"
+ <TableLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="inherit"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="firstStrong"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="anyRtl"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="ltr"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_text"
- android:textDirection="rtl"
- />
- </LinearLayout>
+ <TableRow>
+ <TextView android:text="(unspecified)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
- <LinearLayout android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ <TableRow>
+ <TextView android:text="inherit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="inherit"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="firstStrong"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="anyRtl"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="ltr"
- />
- <TextView android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="24dip"
- android:text="@string/textview_hebrew_text"
- android:textDirection="rtl"
- />
- </LinearLayout>
+ <TableRow>
+ <TextView android:text="firstStrong"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="firstStrong"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="firstStrong"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="firstStrong"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="anyRtl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="anyRtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="anyRtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="anyRtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="ltr"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="ltr"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="ltr"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="ltr"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="rtl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_hebrew_text"
+ android:textDirection="rtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_latin_text"
+ android:textDirection="rtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="24dip"
+ android:text="@string/textview_multiline_text"
+ android:textDirection="rtl"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
- </LinearLayout>
+ </TableLayout>
-</FrameLayout> \ No newline at end of file
+</FrameLayout>
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index c0bbe94..9a486c1 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -40,6 +40,6 @@
<string name="menu_delete">Delete</string>
<string name="textview_hebrew_text">&#x05DD;&#x05DE;ab?!</string>
<string name="textview_latin_text">ab&#x05DD;&#x05DE;?!</string>
- <string name="textview_multiline_text">&#x05DD;&#x05DE;?!\nab?!</string>
+ <string name="textview_multiline_text">&#x05DD;&#x05DE;?!\nab?!\n?!</string>
</resources>