Implement textDirection heuristic selection.

Change-Id: I2fcf18de573f2d66494fa5ed61e4273c3c6078c7
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 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 @@
      * 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 @@
                     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 @@
                          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 @@
             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.ReplacementSpan;
 import android.text.style.TabStopSpan;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.util.Arrays;
 
 /**
@@ -113,6 +113,29 @@
     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 @@
         mSpacingMult = spacingMult;
         mSpacingAdd = spacingAdd;
         mSpannedText = text instanceof Spanned;
+        mTextDir = textDir;
     }
 
     /**
@@ -531,6 +555,14 @@
     }
 
     /**
+     * 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 @@
         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 @@
     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 @@
      * 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 @@
             }
         }
 
-        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.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 @@
              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 @@
              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 @@
                 : (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 @@
 
         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 @@
 
     /* 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 @@
                     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 @@
                 }
             }
 
-            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.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 @@
      * 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 @@
 
         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 @@
                                               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 @@
             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 @@
                     }
 
                     // 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 @@
     }
 
     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.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.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 @@
     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 @@
     /**
      * 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 @@
     /**
      * 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 @@
      * {@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 @@
             @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 @@
      *
      * @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.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 @@
         }
     }
 
-    /**
-     * 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.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.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 @@
         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 @@
                 } 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 @@
             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 @@
                 } 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 @@
         BoringLayout.Metrics boring = UNKNOWN_BORING;
         BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
 
+        if (mTextDir == null) {
+            resolveTextDirection();
+        }
+
         int des = -1;
         boolean fromexisting = false;
 
@@ -5899,7 +5913,7 @@
             }
 
             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 @@
         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 @@
      */
     @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 @@
 
     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 @@
 
         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>
 
-    </LinearLayout>
+            <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>
 
-</FrameLayout>
\ No newline at end of file
+            <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>
+
+        </TableLayout>
+
+</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>
 
-    </LinearLayout>
+            <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>
 
-</FrameLayout>
\ No newline at end of file
+            <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>
+
+        </TableLayout>
+
+</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>