summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/text/ClientFlags.java7
-rw-r--r--core/java/android/text/MeasuredParagraph.java103
-rw-r--r--core/java/android/text/TextFlags.java2
-rw-r--r--core/java/android/text/flags/flags.aconfig7
4 files changed, 119 insertions, 0 deletions
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index 0421d5aaa69b..32f05be5a83a 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -54,4 +54,11 @@ public class ClientFlags {
public static boolean fixLineHeightForLocale() {
return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE);
}
+
+ /**
+ * @see Flags#icuBidiMigration()
+ */
+ public static boolean icuBidiMigration() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_ICU_BIDI_MIGRATION);
+ }
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index b268c2edd9a7..09f15c3aca3e 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -30,6 +30,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.text.LineBreakConfig;
import android.graphics.text.MeasuredText;
+import android.icu.text.Bidi;
import android.text.AutoGrowArray.ByteArray;
import android.text.AutoGrowArray.FloatArray;
import android.text.AutoGrowArray.IntArray;
@@ -115,6 +116,11 @@ public class MeasuredParagraph {
// This is empty if mLtrWithoutBidi is true.
private @NonNull ByteArray mLevels = new ByteArray();
+ // The bidi level for runs.
+ private @NonNull ByteArray mRunLevels = new ByteArray();
+
+ private Bidi mBidi;
+
// The whole width of the text.
// See getWholeWidth comments.
private @FloatRange(from = 0.0f) float mWholeWidth;
@@ -148,6 +154,7 @@ public class MeasuredParagraph {
reset();
mLevels.clearWithReleasingLargeArray();
mWidths.clearWithReleasingLargeArray();
+ mRunLevels.clearWithReleasingLargeArray();
mFontMetrics.clearWithReleasingLargeArray();
mSpanEndCache.clearWithReleasingLargeArray();
}
@@ -160,10 +167,12 @@ public class MeasuredParagraph {
mCopiedBuffer = null;
mWholeWidth = 0;
mLevels.clear();
+ mRunLevels.clear();
mWidths.clear();
mFontMetrics.clear();
mSpanEndCache.clear();
mMeasuredText = null;
+ mBidi = null;
}
/**
@@ -193,6 +202,13 @@ public class MeasuredParagraph {
* @hide
*/
public @Layout.Direction int getParagraphDir() {
+ if (ClientFlags.icuBidiMigration()) {
+ if (mBidi == null) {
+ return Layout.DIR_LEFT_TO_RIGHT;
+ }
+ return (mBidi.getParaLevel() & 0x01) == 0
+ ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
+ }
return mParaDir;
}
@@ -204,6 +220,62 @@ public class MeasuredParagraph {
*/
public Directions getDirections(@IntRange(from = 0) int start, // inclusive
@IntRange(from = 0) int end) { // exclusive
+ if (ClientFlags.icuBidiMigration()) {
+ // Easy case: mBidi == null means the text is all LTR and no bidi suppot is needed.
+ if (mBidi == null) {
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ }
+
+ // Easy case: If the original text only contains single directionality run, the
+ // substring is only single run.
+ if (start == end) {
+ if ((mBidi.getParaLevel() & 0x01) == 0) {
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ } else {
+ return Layout.DIRS_ALL_RIGHT_TO_LEFT;
+ }
+ }
+
+ // Okay, now we need to generate the line instance.
+ Bidi bidi = mBidi.createLineBidi(start, end);
+
+ // Easy case: If the line instance only contains single directionality run, no need
+ // to reorder visually.
+ if (bidi.getRunCount() == 1) {
+ if ((bidi.getParaLevel() & 0x01) == 1) {
+ return Layout.DIRS_ALL_RIGHT_TO_LEFT;
+ } else {
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ }
+ }
+
+ // Reorder directionality run visually.
+ mRunLevels.resize(bidi.getRunCount());
+ byte[] levels = mRunLevels.getRawArray();
+ for (int i = 0; i < bidi.getRunCount(); ++i) {
+ levels[i] = (byte) bidi.getRunLevel(i);
+ }
+ int[] visualOrders = Bidi.reorderVisual(levels);
+
+ int[] dirs = new int[bidi.getRunCount() * 2];
+ for (int i = 0; i < bidi.getRunCount(); ++i) {
+ int vIndex;
+ if ((mBidi.getBaseLevel() & 0x01) == 1) {
+ // For the historical reasons, if the base directionality is RTL, the Android
+ // draws from the right, i.e. the visually reordered run needs to be reversed.
+ vIndex = visualOrders[bidi.getRunCount() - i - 1];
+ } else {
+ vIndex = visualOrders[i];
+ }
+
+ // Special packing of dire
+ dirs[i * 2] = bidi.getRunStart(vIndex);
+ dirs[i * 2 + 1] = bidi.getRunLevel(vIndex) << Layout.RUN_LEVEL_SHIFT
+ | (bidi.getRunLimit(vIndex) - dirs[i * 2]);
+ }
+
+ return new Directions(dirs);
+ }
if (mLtrWithoutBidi) {
return Layout.DIRS_ALL_LEFT_TO_RIGHT;
}
@@ -608,6 +680,37 @@ public class MeasuredParagraph {
}
}
+ if (ClientFlags.icuBidiMigration()) {
+ if ((textDir == TextDirectionHeuristics.LTR
+ || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
+ || textDir == TextDirectionHeuristics.ANYRTL_LTR)
+ && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
+ mLevels.clear();
+ mLtrWithoutBidi = true;
+ return;
+ }
+ final int bidiRequest;
+ if (textDir == TextDirectionHeuristics.LTR) {
+ bidiRequest = Bidi.LTR;
+ } else if (textDir == TextDirectionHeuristics.RTL) {
+ bidiRequest = Bidi.RTL;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
+ bidiRequest = Bidi.LEVEL_DEFAULT_LTR;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
+ bidiRequest = Bidi.LEVEL_DEFAULT_RTL;
+ } else {
+ final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
+ bidiRequest = isRtl ? Bidi.RTL : Bidi.LTR;
+ }
+ mBidi = new Bidi(mCopiedBuffer, 0, null, 0, mCopiedBuffer.length, bidiRequest);
+ mLevels.resize(mTextLength);
+ byte[] rawArray = mLevels.getRawArray();
+ for (int i = 0; i < mTextLength; ++i) {
+ rawArray[i] = mBidi.getLevelAt(i);
+ }
+ mLtrWithoutBidi = false;
+ return;
+ }
if ((textDir == TextDirectionHeuristics.LTR
|| textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
|| textDir == TextDirectionHeuristics.ANYRTL_LTR)
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 24663862400d..770e5c97f50a 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -59,6 +59,7 @@ public final class TextFlags {
Flags.FLAG_PHRASE_STRICT_FALLBACK,
Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
+ Flags.FLAG_ICU_BIDI_MIGRATION,
};
/**
@@ -71,6 +72,7 @@ public final class TextFlags {
Flags.phraseStrictFallback(),
Flags.useBoundsForWidth(),
Flags.fixLineHeightForLocale(),
+ Flags.icuBidiMigration(),
};
/**
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index f3e0ea780182..a49aee1d023d 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -103,3 +103,10 @@ flag {
description: "Fix that InputService#onUpdateSelection is not called when insert mode gesture is performed."
bug: "300850862"
}
+
+flag {
+ name: "icu_bidi_migration"
+ namespace: "text"
+ description: "A flag for replacing AndroidBidi with android.icu.text.Bidi."
+ bug: "317144801"
+}