Merge "Replace JNI portion of AndroidBidi with calls to ICU4J"

This commit is contained in:
Roozbeh Pournader
2017-10-19 22:55:57 +00:00
committed by Android (Google) Code Review
6 changed files with 52 additions and 93 deletions

View File

@@ -16,6 +16,11 @@
package android.text;
import android.icu.lang.UCharacter;
import android.icu.lang.UCharacterDirection;
import android.icu.lang.UProperty;
import android.icu.text.Bidi;
import android.icu.text.BidiClassifier;
import android.text.Layout.Directions;
import com.android.internal.annotations.VisibleForTesting;
@@ -27,26 +32,57 @@ import com.android.internal.annotations.VisibleForTesting;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class AndroidBidi {
public static int bidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) {
private static class EmojiBidiOverride extends BidiClassifier {
EmojiBidiOverride() {
super(null /* No persisting object needed */);
}
// Tells ICU to use the standard Unicode value.
private static final int NO_OVERRIDE =
UCharacter.getIntPropertyMaxValue(UProperty.BIDI_CLASS) + 1;
@Override
public int classify(int c) {
if (Emoji.isNewEmoji(c)) {
// All new emoji characters in Unicode 10.0 are of the bidi class ON.
return UCharacterDirection.OTHER_NEUTRAL;
} else {
return NO_OVERRIDE;
}
}
}
private static final EmojiBidiOverride sEmojiBidiOverride = new EmojiBidiOverride();
/**
* Runs the bidi algorithm on input text.
*/
public static int bidi(int dir, char[] chs, byte[] chInfo) {
if (chs == null || chInfo == null) {
throw new NullPointerException();
}
if (n < 0 || chs.length < n || chInfo.length < n) {
final int length = chs.length;
if (chInfo.length < length) {
throw new IndexOutOfBoundsException();
}
switch(dir) {
case Layout.DIR_REQUEST_LTR: dir = 0; break;
case Layout.DIR_REQUEST_RTL: dir = 1; break;
case Layout.DIR_REQUEST_DEFAULT_LTR: dir = -2; break;
case Layout.DIR_REQUEST_DEFAULT_RTL: dir = -1; break;
default: dir = 0; break;
final byte paraLevel;
switch (dir) {
case Layout.DIR_REQUEST_LTR: paraLevel = Bidi.LTR; break;
case Layout.DIR_REQUEST_RTL: paraLevel = Bidi.RTL; break;
case Layout.DIR_REQUEST_DEFAULT_LTR: paraLevel = Bidi.LEVEL_DEFAULT_LTR; break;
case Layout.DIR_REQUEST_DEFAULT_RTL: paraLevel = Bidi.LEVEL_DEFAULT_RTL; break;
default: paraLevel = Bidi.LTR; break;
}
int result = runBidi(dir, chs, chInfo, n, haveInfo);
result = (result & 0x1) == 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
return result;
final Bidi icuBidi = new Bidi(length /* maxLength */, 0 /* maxRunCount */);
icuBidi.setCustomClassifier(sEmojiBidiOverride);
icuBidi.setPara(chs, paraLevel, null /* embeddingLevels */);
for (int i = 0; i < length; i++) {
chInfo[i] = icuBidi.getLevelAt(i);
}
final byte result = icuBidi.getParaLevel();
return (result & 0x1) == 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
}
/**
@@ -178,6 +214,4 @@ public class AndroidBidi {
}
return new Directions(ld);
}
private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo);
}

View File

@@ -106,8 +106,8 @@ class MeasuredText {
if (mWidths == null || mWidths.length < len) {
mWidths = ArrayUtils.newUnpaddedFloatArray(len);
}
if (mChars == null || mChars.length < len) {
mChars = ArrayUtils.newUnpaddedCharArray(len);
if (mChars == null || mChars.length != len) {
mChars = new char[len];
}
TextUtils.getChars(text, start, end, mChars, 0);
@@ -151,7 +151,7 @@ class MeasuredText {
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);
mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels);
mEasy = false;
}
}

View File

@@ -87,7 +87,6 @@ cc_library_shared {
"android_view_ThreadedRenderer.cpp",
"android_view_VelocityTracker.cpp",
"android_text_AndroidCharacter.cpp",
"android_text_AndroidBidi.cpp",
"android_text_Hyphenator.cpp",
"android_text_StaticLayout.cpp",
"android_os_Debug.cpp",

View File

@@ -177,7 +177,6 @@ extern int register_android_net_TrafficStats(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
extern int register_android_text_StaticLayout(JNIEnv *env);
extern int register_android_text_AndroidBidi(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
@@ -1335,7 +1334,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_text_StaticLayout),
REG_JNI(register_android_text_AndroidBidi),
REG_JNI(register_android_view_InputDevice),
REG_JNI(register_android_view_KeyCharacterMap),
REG_JNI(register_android_os_Process),

View File

@@ -1,72 +0,0 @@
/* //device/libs/android_runtime/android_text_AndroidBidi.cpp
**
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "AndroidUnicode"
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
#include "utils/misc.h"
#include "utils/Log.h"
#include "unicode/ubidi.h"
#include <minikin/Emoji.h>
namespace android {
static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray,
jbyteArray infoArray, jint n, jboolean haveInfo)
{
// Parameters are checked on java side
// Failures from GetXXXArrayElements indicate a serious out-of-memory condition
// that we don't bother to report, we're probably dead anyway.
jint result = 0;
jchar* chs = env->GetCharArrayElements(chsArray, NULL);
if (chs != NULL) {
jbyte* info = env->GetByteArrayElements(infoArray, NULL);
if (info != NULL) {
UErrorCode status = U_ZERO_ERROR;
UBiDi* bidi = ubidi_openSized(n, 0, &status);
// Set callbacks to override bidi classes of new emoji
ubidi_setClassCallback(
bidi, minikin::emojiBidiOverride, nullptr, nullptr, nullptr, &status);
ubidi_setPara(bidi, reinterpret_cast<const UChar*>(chs), n, dir, NULL, &status);
if (U_SUCCESS(status)) {
for (int i = 0; i < n; ++i) {
info[i] = ubidi_getLevelAt(bidi, i);
}
result = ubidi_getParaLevel(bidi);
} else {
jniThrowException(env, "java/lang/RuntimeException", NULL);
}
ubidi_close(bidi);
env->ReleaseByteArrayElements(infoArray, info, 0);
}
env->ReleaseCharArrayElements(chsArray, chs, JNI_ABORT);
}
return result;
}
static const JNINativeMethod gMethods[] = {
{ "runBidi", "(I[C[BIZ)I", (void*) runBidi }
};
int register_android_text_AndroidBidi(JNIEnv* env)
{
return RegisterMethodsOrDie(env, "android/text/AndroidBidi", gMethods, NELEM(gMethods));
}
}

View File

@@ -96,7 +96,7 @@ public class StaticLayoutBidiTest {
int n = chs.length;
byte[] chInfo = new byte[n];
int resultDir = AndroidBidi.bidi(dir, chs, chInfo, n, false);
int resultDir = AndroidBidi.bidi(dir, chs, chInfo);
{
StringBuilder sb = new StringBuilder("info:");