summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/view/View.java22
-rw-r--r--core/java/android/view/ViewGroup.java1
-rw-r--r--core/java/android/widget/TextView.java44
-rwxr-xr-xcore/res/res/values/attrs.xml12
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java34
6 files changed, 107 insertions, 7 deletions
diff --git a/api/current.txt b/api/current.txt
index 03c654ebe55c..c23b28b27c5d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22069,6 +22069,7 @@ package android.view {
method public boolean willNotDraw();
field public static android.util.Property ALPHA;
field protected static int DEFAULT_TEXT_DIRECTION;
+ field protected static float DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD;
field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bf7f359303ef..8a72c4a14c66 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2523,18 +2523,26 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
public static final int TEXT_DIRECTION_ANY_RTL = 2;
/**
+ * Text direction is the same as the one held by a 60% majority of the characters. If there is
+ * no majority then the paragraph direction is the resolved layout direction of the View.
+ *
+ * @hide
+ */
+ public static final int TEXT_DIRECTION_CHAR_COUNT = 3;
+
+ /**
* Text direction is forced to LTR.
*
* @hide
*/
- public static final int TEXT_DIRECTION_LTR = 3;
+ public static final int TEXT_DIRECTION_LTR = 4;
/**
* Text direction is forced to RTL.
*
* @hide
*/
- public static final int TEXT_DIRECTION_RTL = 4;
+ public static final int TEXT_DIRECTION_RTL = 5;
/**
* Default text direction is inherited
@@ -2542,6 +2550,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
/**
+ * Default threshold for "char count" heuristic.
+ */
+ protected static float DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD = 0.6f;
+
+ /**
* The text direction that has been defined by {@link #setTextDirection(int)}.
*
* {@hide}
@@ -2551,6 +2564,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
@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")
})
@@ -11963,7 +11977,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
- if (mLayoutParams != null) {
+ if (mLayoutParams != null && mParent != null) {
mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
}
@@ -12990,6 +13004,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
* {@link #TEXT_DIRECTION_INHERIT},
* {@link #TEXT_DIRECTION_FIRST_STRONG}
* {@link #TEXT_DIRECTION_ANY_RTL},
+ * {@link #TEXT_DIRECTION_CHAR_COUNT},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
*
@@ -13007,6 +13022,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
* {@link #TEXT_DIRECTION_INHERIT},
* {@link #TEXT_DIRECTION_FIRST_STRONG}
* {@link #TEXT_DIRECTION_ANY_RTL},
+ * {@link #TEXT_DIRECTION_CHAR_COUNT},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
*
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2a90ddea3b55..25ca42178417 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5039,6 +5039,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// 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;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f79dabd9a808..04b79c5ef8e6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10039,6 +10039,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case TEXT_DIRECTION_ANY_RTL:
resolvedTextDirection = getTextDirectionFromAnyRtl(mText);
break;
+ case TEXT_DIRECTION_CHAR_COUNT:
+ resolvedTextDirection = getTextDirectionFromCharCount(mText);
+ break;
case TEXT_DIRECTION_LTR:
resolvedTextDirection = TEXT_DIRECTION_LTR;
break;
@@ -10072,6 +10075,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
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);
@@ -10094,6 +10100,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
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++) {
@@ -10115,6 +10124,41 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * 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).
*/
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index fd61cfdaf6b7..65f58421c9b3 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1985,10 +1985,14 @@
it is LTR if it contains any strong LTR characters. If there are neither, the
paragraph direction is the view’s resolved layout direction. -->
<enum name="anyRtl" value="2" />
- <!-- The text direction is left to right. -->
- <enum name="ltr" value="3" />
- <!-- The text direction is right to left. -->
- <enum name="rtl" value="4" />
+ <!-- The paragraph direction is the same as the one held by a 60% majority of the
+ characters. If there is no majority then the paragraph direction is the resolved
+ layout direction of the View. -->
+ <enum name="charCount" value="3" />
+ <!-- The paragraph direction is left to right. -->
+ <enum name="ltr" value="4" />
+ <!-- The paragraph direction is right to left. -->
+ <enum name="rtl" value="5" />
</attr>
</declare-styleable>
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index a37f1a37e8dc..6db67c0de787 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -177,4 +177,38 @@ public class TextViewTest extends AndroidTestCase {
tv.setTextDirection(View.TEXT_DIRECTION_RTL);
assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
}
+
+ @SmallTest
+ public void testCharCountHeuristic() {
+ LinearLayout ll = new LinearLayout(mContext);
+ ll.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+
+ TextView tv = new TextView(mContext);
+ ll.addView(tv);
+
+ tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+ tv.setText("this is a test");
+ assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+ // resetResolvedTextDirection is not part of the public API so simply use setTextDirection
+ tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+ tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+ tv.setText("\u05DD\u05DE"); // hebrew
+ assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+ tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+ tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+ tv.setText("this is a test \u05DD\u05DE"); // latin more than 60% + hebrew
+ assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+ tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+ tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+ tv.setText("t \u05DD\u05DE"); // latin + hebrew more than 60%
+ assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+ tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+ tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+ tv.setText("ab \u05DD\u05DE"); // latin + hebrew at 50% each
+ assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+ }
}