Merge pull request #90 from alexandrius/master

Fix Georgian automatic uppercase since Georgian script doesn't have uppercase
This commit is contained in:
Daniele Laudani 2020-04-23 23:09:22 +02:00 committed by GitHub
commit 363e27dc5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 234 additions and 200 deletions

View file

@ -36,6 +36,7 @@ import org.dslul.openboard.inputmethod.keyboard.internal.UniqueKeysCache;
import org.dslul.openboard.inputmethod.latin.InputAttributes; import org.dslul.openboard.inputmethod.latin.InputAttributes;
import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.RichInputMethodSubtype; import org.dslul.openboard.inputmethod.latin.RichInputMethodSubtype;
import org.dslul.openboard.inputmethod.latin.common.StringUtils;
import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils; import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils;
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils; import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils; import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils;
@ -104,7 +105,9 @@ public final class KeyboardLayoutSet {
boolean mProximityCharsCorrectionEnabled; boolean mProximityCharsCorrectionEnabled;
boolean mSupportsSplitLayout; boolean mSupportsSplitLayout;
boolean mAllowRedundantMoreKeys; boolean mAllowRedundantMoreKeys;
public ElementParams() {}
public ElementParams() {
}
} }
public static final class Params { public static final class Params {
@ -147,7 +150,7 @@ public final class KeyboardLayoutSet {
} }
public static int getScriptId(final Resources resources, public static int getScriptId(final Resources resources,
@Nonnull final InputMethodSubtype subtype) { @Nonnull final InputMethodSubtype subtype) {
final Integer value = sScriptIdsForSubtypes.get(subtype); final Integer value = sScriptIdsForSubtypes.get(subtype);
if (null == value) { if (null == value) {
final int scriptId = Builder.readScriptId(resources, subtype); final int scriptId = Builder.readScriptId(resources, subtype);
@ -162,26 +165,28 @@ public final class KeyboardLayoutSet {
mParams = params; mParams = params;
} }
public static final String LOCALE_GEORGIAN = "ka";
@Nonnull @Nonnull
public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) { public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) {
final int keyboardLayoutSetElementId; final int keyboardLayoutSetElementId;
switch (mParams.mMode) { switch (mParams.mMode) {
case KeyboardId.MODE_PHONE: case KeyboardId.MODE_PHONE:
if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) { if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) {
keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS; keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS;
} else { } else {
keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE; keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE;
} }
break; break;
case KeyboardId.MODE_NUMBER: case KeyboardId.MODE_NUMBER:
case KeyboardId.MODE_DATE: case KeyboardId.MODE_DATE:
case KeyboardId.MODE_TIME: case KeyboardId.MODE_TIME:
case KeyboardId.MODE_DATETIME: case KeyboardId.MODE_DATETIME:
keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER; keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER;
break; break;
default: default:
keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId; keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId;
break; break;
} }
ElementParams elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get( ElementParams elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get(
@ -197,6 +202,7 @@ public final class KeyboardLayoutSet {
mParams.mIsSplitLayoutEnabled = mParams.mIsSplitLayoutEnabledByUser mParams.mIsSplitLayoutEnabled = mParams.mIsSplitLayoutEnabledByUser
&& elementParams.mSupportsSplitLayout; && elementParams.mSupportsSplitLayout;
final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams); final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams);
try { try {
return getKeyboard(elementParams, id); return getKeyboard(elementParams, id);
@ -279,8 +285,7 @@ public final class KeyboardLayoutSet {
// be locked down. // be locked down.
// TODO: Switch to {@code UserManagerCompat.isUserUnlocked()} in the support-v4 library // TODO: Switch to {@code UserManagerCompat.isUserUnlocked()} in the support-v4 library
// when it becomes publicly available. // when it becomes publicly available.
@UserManagerCompatUtils.LockState @UserManagerCompatUtils.LockState final int lockState = UserManagerCompatUtils.getUserLockState(context);
final int lockState = UserManagerCompatUtils.getUserLockState(context);
if (lockState == UserManagerCompatUtils.LOCK_STATE_LOCKED) { if (lockState == UserManagerCompatUtils.LOCK_STATE_LOCKED) {
params.mNoSettingsKey = true; params.mNoSettingsKey = true;
} }
@ -295,8 +300,7 @@ public final class KeyboardLayoutSet {
public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) { public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) {
final boolean asciiCapable = subtype.getmSubtype().isAsciiCapable(); final boolean asciiCapable = subtype.getmSubtype().isAsciiCapable();
// TODO: Consolidate with {@link InputAttributes}. // TODO: Consolidate with {@link InputAttributes}.
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation") final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
mPackageName, FORCE_ASCII, mParams.mEditorInfo); mPackageName, FORCE_ASCII, mParams.mEditorInfo);
final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii( final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(
mParams.mEditorInfo.imeOptions) mParams.mEditorInfo.imeOptions)
@ -361,7 +365,7 @@ public final class KeyboardLayoutSet {
} }
private static int readScriptIdFromTagFeature(final Resources resources, private static int readScriptIdFromTagFeature(final Resources resources,
final XmlPullParser parser) throws IOException, XmlPullParserException { final XmlPullParser parser) throws IOException, XmlPullParserException {
final TypedArray featureAttr = resources.obtainAttributes(Xml.asAttributeSet(parser), final TypedArray featureAttr = resources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.KeyboardLayoutSet_Feature); R.styleable.KeyboardLayoutSet_Feature);
try { try {
@ -473,33 +477,33 @@ public final class KeyboardLayoutSet {
final int variation = inputType & InputType.TYPE_MASK_VARIATION; final int variation = inputType & InputType.TYPE_MASK_VARIATION;
switch (inputType & InputType.TYPE_MASK_CLASS) { switch (inputType & InputType.TYPE_MASK_CLASS) {
case InputType.TYPE_CLASS_NUMBER: case InputType.TYPE_CLASS_NUMBER:
return KeyboardId.MODE_NUMBER; return KeyboardId.MODE_NUMBER;
case InputType.TYPE_CLASS_DATETIME: case InputType.TYPE_CLASS_DATETIME:
switch (variation) { switch (variation) {
case InputType.TYPE_DATETIME_VARIATION_DATE: case InputType.TYPE_DATETIME_VARIATION_DATE:
return KeyboardId.MODE_DATE; return KeyboardId.MODE_DATE;
case InputType.TYPE_DATETIME_VARIATION_TIME: case InputType.TYPE_DATETIME_VARIATION_TIME:
return KeyboardId.MODE_TIME; return KeyboardId.MODE_TIME;
default: // InputType.TYPE_DATETIME_VARIATION_NORMAL default: // InputType.TYPE_DATETIME_VARIATION_NORMAL
return KeyboardId.MODE_DATETIME; return KeyboardId.MODE_DATETIME;
} }
case InputType.TYPE_CLASS_PHONE: case InputType.TYPE_CLASS_PHONE:
return KeyboardId.MODE_PHONE; return KeyboardId.MODE_PHONE;
case InputType.TYPE_CLASS_TEXT: case InputType.TYPE_CLASS_TEXT:
if (InputTypeUtils.isEmailVariation(variation)) { if (InputTypeUtils.isEmailVariation(variation)) {
return KeyboardId.MODE_EMAIL; return KeyboardId.MODE_EMAIL;
} else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
return KeyboardId.MODE_URL; return KeyboardId.MODE_URL;
} else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
return KeyboardId.MODE_IM; return KeyboardId.MODE_IM;
} else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
return KeyboardId.MODE_TEXT;
} else {
return KeyboardId.MODE_TEXT;
}
default:
return KeyboardId.MODE_TEXT; return KeyboardId.MODE_TEXT;
} else {
return KeyboardId.MODE_TEXT;
}
default:
return KeyboardId.MODE_TEXT;
} }
} }
} }

View file

@ -16,7 +16,10 @@
package org.dslul.openboard.inputmethod.latin.common; package org.dslul.openboard.inputmethod.latin.common;
import android.renderscript.Script;
import org.dslul.openboard.inputmethod.annotations.UsedForTesting; import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -26,6 +29,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public final class StringUtils { public final class StringUtils {
public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
public static final int CAPITALIZE_FIRST = 1; // First only public static final int CAPITALIZE_FIRST = 1; // First only
public static final int CAPITALIZE_ALL = 2; // All caps public static final int CAPITALIZE_ALL = 2; // All caps
@ -47,8 +51,10 @@ public final class StringUtils {
// Taken from android.text.TextUtils. We are extensively using this method in many places, // Taken from android.text.TextUtils. We are extensively using this method in many places,
// some of which don't have the android libraries available. // some of which don't have the android libraries available.
/** /**
* Returns true if the string is null or 0-length. * Returns true if the string is null or 0-length.
*
* @param str the string to be examined * @param str the string to be examined
* @return true if str is null or zero length * @return true if str is null or zero length
*/ */
@ -57,18 +63,20 @@ public final class StringUtils {
} }
// Taken from android.text.TextUtils to cut the dependency to the Android framework. // Taken from android.text.TextUtils to cut the dependency to the Android framework.
/** /**
* Returns a string containing the tokens joined by delimiters. * Returns a string containing the tokens joined by delimiters.
*
* @param delimiter the delimiter * @param delimiter the delimiter
* @param tokens an array objects to be joined. Strings will be formed from * @param tokens an array objects to be joined. Strings will be formed from
* the objects by calling object.toString(). * the objects by calling object.toString().
*/ */
@Nonnull @Nonnull
public static String join(@Nonnull final CharSequence delimiter, public static String join(@Nonnull final CharSequence delimiter,
@Nonnull final Iterable<?> tokens) { @Nonnull final Iterable<?> tokens) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
boolean firstTime = true; boolean firstTime = true;
for (final Object token: tokens) { for (final Object token : tokens) {
if (firstTime) { if (firstTime) {
firstTime = false; firstTime = false;
} else { } else {
@ -80,10 +88,12 @@ public final class StringUtils {
} }
// Taken from android.text.TextUtils to cut the dependency to the Android framework. // Taken from android.text.TextUtils to cut the dependency to the Android framework.
/** /**
* Returns true if a and b are equal, including if they are both null. * Returns true if a and b are equal, including if they are both null.
* <p><i>Note: In platform versions 1.1 and earlier, this method only worked well if * <p><i>Note: In platform versions 1.1 and earlier, this method only worked well if
* both the arguments were instances of String.</i></p> * both the arguments were instances of String.</i></p>
*
* @param a first CharSequence to check * @param a first CharSequence to check
* @param b second CharSequence to check * @param b second CharSequence to check
* @return true if a and b are equal * @return true if a and b are equal
@ -126,7 +136,7 @@ public final class StringUtils {
} }
public static boolean containsInArray(@Nonnull final String text, public static boolean containsInArray(@Nonnull final String text,
@Nonnull final String[] array) { @Nonnull final String[] array) {
for (final String element : array) { for (final String element : array) {
if (text.equals(element)) { if (text.equals(element)) {
return true; return true;
@ -144,7 +154,7 @@ public final class StringUtils {
private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ","; private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ",";
public static boolean containsInCommaSplittableText(@Nonnull final String text, public static boolean containsInCommaSplittableText(@Nonnull final String text,
@Nullable final String extraValues) { @Nullable final String extraValues) {
if (isEmpty(extraValues)) { if (isEmpty(extraValues)) {
return false; return false;
} }
@ -153,7 +163,7 @@ public final class StringUtils {
@Nonnull @Nonnull
public static String removeFromCommaSplittableTextIfExists(@Nonnull final String text, public static String removeFromCommaSplittableTextIfExists(@Nonnull final String text,
@Nullable final String extraValues) { @Nullable final String extraValues) {
if (isEmpty(extraValues)) { if (isEmpty(extraValues)) {
return EMPTY_STRING; return EMPTY_STRING;
} }
@ -172,7 +182,7 @@ public final class StringUtils {
/** /**
* Remove duplicates from an array of strings. * Remove duplicates from an array of strings.
* * <p>
* This method will always keep the first occurrence of all strings at their position * This method will always keep the first occurrence of all strings at their position
* in the array, removing the subsequent ones. * in the array, removing the subsequent ones.
*/ */
@ -199,7 +209,7 @@ public final class StringUtils {
@Nonnull @Nonnull
public static String capitalizeFirstCodePoint(@Nonnull final String s, public static String capitalizeFirstCodePoint(@Nonnull final String s,
@Nonnull final Locale locale) { @Nonnull final Locale locale) {
if (s.length() <= 1) { if (s.length() <= 1) {
return s.toUpperCase(getLocaleUsedForToTitleCase(locale)); return s.toUpperCase(getLocaleUsedForToTitleCase(locale));
} }
@ -212,7 +222,7 @@ public final class StringUtils {
@Nonnull @Nonnull
public static String capitalizeFirstAndDowncaseRest(@Nonnull final String s, public static String capitalizeFirstAndDowncaseRest(@Nonnull final String s,
@Nonnull final Locale locale) { @Nonnull final Locale locale) {
if (s.length() <= 1) { if (s.length() <= 1) {
return s.toUpperCase(getLocaleUsedForToTitleCase(locale)); return s.toUpperCase(getLocaleUsedForToTitleCase(locale));
} }
@ -238,14 +248,15 @@ public final class StringUtils {
/** /**
* Converts a range of a string to an array of code points. * Converts a range of a string to an array of code points.
*
* @param charSequence the source string. * @param charSequence the source string.
* @param startIndex the start index inside the string in java chars, inclusive. * @param startIndex the start index inside the string in java chars, inclusive.
* @param endIndex the end index inside the string in java chars, exclusive. * @param endIndex the end index inside the string in java chars, exclusive.
* @return a new array of code points. At most endIndex - startIndex, but possibly less. * @return a new array of code points. At most endIndex - startIndex, but possibly less.
*/ */
@Nonnull @Nonnull
public static int[] toCodePointArray(@Nonnull final CharSequence charSequence, public static int[] toCodePointArray(@Nonnull final CharSequence charSequence,
final int startIndex, final int endIndex) { final int startIndex, final int endIndex) {
final int length = charSequence.length(); final int length = charSequence.length();
if (length <= 0) { if (length <= 0) {
return EMPTY_CODEPOINTS; return EMPTY_CODEPOINTS;
@ -259,7 +270,7 @@ public final class StringUtils {
/** /**
* Copies the codepoints in a CharSequence to an int array. * Copies the codepoints in a CharSequence to an int array.
* * <p>
* This method assumes there is enough space in the array to store the code points. The size * This method assumes there is enough space in the array to store the code points. The size
* can be measured with Character#codePointCount(CharSequence, int, int) before passing to this * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this
* method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown. * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown.
@ -268,19 +279,19 @@ public final class StringUtils {
* This method can optionally downcase code points before copying them, but it pays no attention * This method can optionally downcase code points before copying them, but it pays no attention
* to locale while doing so. * to locale while doing so.
* *
* @param destination the int array. * @param destination the int array.
* @param charSequence the CharSequence. * @param charSequence the CharSequence.
* @param startIndex the start index inside the string in java chars, inclusive. * @param startIndex the start index inside the string in java chars, inclusive.
* @param endIndex the end index inside the string in java chars, exclusive. * @param endIndex the end index inside the string in java chars, exclusive.
* @param downCase if this is true, code points will be downcased before being copied. * @param downCase if this is true, code points will be downcased before being copied.
* @return the number of copied code points. * @return the number of copied code points.
*/ */
public static int copyCodePointsAndReturnCodePointCount(@Nonnull final int[] destination, public static int copyCodePointsAndReturnCodePointCount(@Nonnull final int[] destination,
@Nonnull final CharSequence charSequence, final int startIndex, final int endIndex, @Nonnull final CharSequence charSequence, final int startIndex, final int endIndex,
final boolean downCase) { final boolean downCase) {
int destIndex = 0; int destIndex = 0;
for (int index = startIndex; index < endIndex; for (int index = startIndex; index < endIndex;
index = Character.offsetByCodePoints(charSequence, index, 1)) { index = Character.offsetByCodePoints(charSequence, index, 1)) {
final int codePoint = Character.codePointAt(charSequence, index); final int codePoint = Character.codePointAt(charSequence, index);
// TODO: stop using this, as it's not aware of the locale and does not always do // TODO: stop using this, as it's not aware of the locale and does not always do
// the right thing. // the right thing.
@ -301,7 +312,7 @@ public final class StringUtils {
* Construct a String from a code point array * Construct a String from a code point array
* *
* @param codePoints a code point array that is null terminated when its logical length is * @param codePoints a code point array that is null terminated when its logical length is
* shorter than the array length. * shorter than the array length.
* @return a string constructed from the code point array. * @return a string constructed from the code point array.
*/ */
@Nonnull @Nonnull
@ -335,7 +346,7 @@ public final class StringUtils {
int capsCount = 1; int capsCount = 1;
int letterCount = 1; int letterCount = 1;
for (index = text.offsetByCodePoints(index, 1); index < len; for (index = text.offsetByCodePoints(index, 1); index < len;
index = text.offsetByCodePoints(index, 1)) { index = text.offsetByCodePoints(index, 1)) {
if (1 != capsCount && letterCount != capsCount) break; if (1 != capsCount && letterCount != capsCount) break;
final int codePoint = text.codePointAt(index); final int codePoint = text.codePointAt(index);
if (Character.isUpperCase(codePoint)) { if (Character.isUpperCase(codePoint)) {
@ -381,7 +392,7 @@ public final class StringUtils {
} }
public static boolean isIdenticalAfterCapitalizeEachWord(@Nonnull final String text, public static boolean isIdenticalAfterCapitalizeEachWord(@Nonnull final String text,
@Nonnull final int[] sortedSeparators) { @Nonnull final int[] sortedSeparators) {
boolean needsCapsNext = true; boolean needsCapsNext = true;
final int len = text.length(); final int len = text.length();
for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) { for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
@ -402,7 +413,7 @@ public final class StringUtils {
// which should be capitalized together in *some* cases. // which should be capitalized together in *some* cases.
@Nonnull @Nonnull
public static String capitalizeEachWord(@Nonnull final String text, public static String capitalizeEachWord(@Nonnull final String text,
@Nonnull final int[] sortedSeparators, @Nonnull final Locale locale) { @Nonnull final int[] sortedSeparators, @Nonnull final Locale locale) {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
boolean needsCapsNext = true; boolean needsCapsNext = true;
final int len = text.length(); final int len = text.length();
@ -421,7 +432,7 @@ public final class StringUtils {
/** /**
* Approximates whether the text before the cursor looks like a URL. * Approximates whether the text before the cursor looks like a URL.
* * <p>
* This is not foolproof, but it should work well in the practice. * This is not foolproof, but it should work well in the practice.
* Essentially it walks backward from the cursor until it finds something that's not a letter, * Essentially it walks backward from the cursor until it finds something that's not a letter,
* digit, or common URL symbol like underscore. If it hasn't found a period yet, then it * digit, or common URL symbol like underscore. If it hasn't found a period yet, then it
@ -430,9 +441,9 @@ public final class StringUtils {
* - starts with www and contains a period * - starts with www and contains a period
* - starts with a slash preceded by either a slash, whitespace, or start-of-string * - starts with a slash preceded by either a slash, whitespace, or start-of-string
* Then it looks like a URL and we return true. Otherwise, we return false. * Then it looks like a URL and we return true. Otherwise, we return false.
* * <p>
* Note: this method is called quite often, and should be fast. * Note: this method is called quite often, and should be fast.
* * <p>
* TODO: This will return that "abc./def" and ".abc/def" look like URLs to keep down the * TODO: This will return that "abc./def" and ".abc/def" look like URLs to keep down the
* code complexity, but ideally it should not. It's acceptable for now. * code complexity, but ideally it should not. It's acceptable for now.
*/ */
@ -491,7 +502,7 @@ public final class StringUtils {
/** /**
* Examines the string and returns whether we're inside a double quote. * Examines the string and returns whether we're inside a double quote.
* * <p>
* This is used to decide whether we should put an automatic space before or after a double * This is used to decide whether we should put an automatic space before or after a double
* quote character. If we're inside a quotation, then we want to close it, so we want a space * quote character. If we're inside a quotation, then we want to close it, so we want a space
* after and not before. Otherwise, we want to open the quotation, so we want a space before * after and not before. Otherwise, we want to open the quotation, so we want a space before
@ -596,10 +607,11 @@ public final class StringUtils {
@Nullable @Nullable
public static String toTitleCaseOfKeyLabel(@Nullable final String label, public static String toTitleCaseOfKeyLabel(@Nullable final String label,
@Nonnull final Locale locale) { @Nonnull final Locale locale) {
if (label == null) { if (label == null || !ScriptUtils.scriptSupportsUppercase(locale.getLanguage())) {
return label; return label;
} }
return label.toUpperCase(getLocaleUsedForToTitleCase(locale)); return label.toUpperCase(getLocaleUsedForToTitleCase(locale));
} }
@ -661,7 +673,7 @@ public final class StringUtils {
@Nonnull @Nonnull
protected String joinStringArray(@Nonnull final String[] stringArray, protected String joinStringArray(@Nonnull final String[] stringArray,
@Nullable final String delimiter) { @Nullable final String delimiter) {
if (delimiter == null) { if (delimiter == null) {
return Arrays.toString(stringArray); return Arrays.toString(stringArray);
} }
@ -676,6 +688,7 @@ public final class StringUtils {
/** /**
* Returns whether the last composed word contains line-breaking character (e.g. CR or LF). * Returns whether the last composed word contains line-breaking character (e.g. CR or LF).
*
* @param text the text to be examined. * @param text the text to be examined.
* @return {@code true} if the last composed word contains line-breaking separator. * @return {@code true} if the last composed word contains line-breaking separator.
*/ */

View file

@ -172,7 +172,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public void loadSettings(final Context context, final Locale locale, public void loadSettings(final Context context, final Locale locale,
@Nonnull final InputAttributes inputAttributes) { @Nonnull final InputAttributes inputAttributes) {
mSettingsValuesLock.lock(); mSettingsValuesLock.lock();
mContext = context; mContext = context;
try { try {
@ -204,20 +204,20 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
// Accessed from the settings interface, hence public // Accessed from the settings interface, hence public
public static boolean readKeypressSoundEnabled(final SharedPreferences prefs, public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
final Resources res) { final Resources res) {
return prefs.getBoolean(PREF_SOUND_ON, return prefs.getBoolean(PREF_SOUND_ON,
res.getBoolean(R.bool.config_default_sound_enabled)); res.getBoolean(R.bool.config_default_sound_enabled));
} }
public static boolean readVibrationEnabled(final SharedPreferences prefs, public static boolean readVibrationEnabled(final SharedPreferences prefs,
final Resources res) { final Resources res) {
final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator(); final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator();
return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON, return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON,
res.getBoolean(R.bool.config_default_vibration_enabled)); res.getBoolean(R.bool.config_default_vibration_enabled));
} }
public static boolean readAutoCorrectEnabled(final SharedPreferences prefs, public static boolean readAutoCorrectEnabled(final SharedPreferences prefs,
final Resources res) { final Resources res) {
return prefs.getBoolean(PREF_AUTO_CORRECTION, true); return prefs.getBoolean(PREF_AUTO_CORRECTION, true);
} }
@ -226,7 +226,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs, public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs,
final Resources res) { final Resources res) {
return prefs.getBoolean(PREF_BLOCK_POTENTIALLY_OFFENSIVE, return prefs.getBoolean(PREF_BLOCK_POTENTIALLY_OFFENSIVE,
res.getBoolean(R.bool.config_block_potentially_offensive)); res.getBoolean(R.bool.config_block_potentially_offensive));
} }
@ -239,7 +239,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static boolean readGestureInputEnabled(final SharedPreferences prefs, public static boolean readGestureInputEnabled(final SharedPreferences prefs,
final Resources res) { final Resources res) {
return readFromBuildConfigIfGestureInputEnabled(res) return readFromBuildConfigIfGestureInputEnabled(res)
&& prefs.getBoolean(PREF_GESTURE_INPUT, true); && prefs.getBoolean(PREF_GESTURE_INPUT, true);
} }
@ -249,7 +249,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs, public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs,
final Resources res) { final Resources res) {
final boolean defaultKeyPreviewPopup = res.getBoolean( final boolean defaultKeyPreviewPopup = res.getBoolean(
R.bool.config_default_key_preview_popup); R.bool.config_default_key_preview_popup);
if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
@ -259,26 +259,26 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs, public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs,
final Resources res) { final Resources res) {
return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
Integer.toString(res.getInteger( Integer.toString(res.getInteger(
R.integer.config_key_preview_linger_timeout)))); R.integer.config_key_preview_linger_timeout))));
} }
public static String readPrefAdditionalSubtypes(final SharedPreferences prefs, public static String readPrefAdditionalSubtypes(final SharedPreferences prefs,
final Resources res) { final Resources res) {
final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes( final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(
res.getStringArray(R.array.predefined_subtypes)); res.getStringArray(R.array.predefined_subtypes));
return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes); return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes);
} }
public static void writePrefAdditionalSubtypes(final SharedPreferences prefs, public static void writePrefAdditionalSubtypes(final SharedPreferences prefs,
final String prefSubtypes) { final String prefSubtypes) {
prefs.edit().putString(PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply(); prefs.edit().putString(PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply();
} }
public static float readKeypressSoundVolume(final SharedPreferences prefs, public static float readKeypressSoundVolume(final SharedPreferences prefs,
final Resources res) { final Resources res) {
final float volume = prefs.getFloat( final float volume = prefs.getFloat(
PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT); PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT);
return (volume != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? volume return (volume != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? volume
@ -295,7 +295,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static int readKeyLongpressTimeout(final SharedPreferences prefs, public static int readKeyLongpressTimeout(final SharedPreferences prefs,
final Resources res) { final Resources res) {
final int milliseconds = prefs.getInt( final int milliseconds = prefs.getInt(
PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT);
return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
@ -307,7 +307,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static int readKeypressVibrationDuration(final SharedPreferences prefs, public static int readKeypressVibrationDuration(final SharedPreferences prefs,
final Resources res) { final Resources res) {
final int milliseconds = prefs.getInt( final int milliseconds = prefs.getInt(
PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT); PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT);
return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
@ -324,19 +324,19 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static float readKeyPreviewAnimationScale(final SharedPreferences prefs, public static float readKeyPreviewAnimationScale(final SharedPreferences prefs,
final String prefKey, final float defaultValue) { final String prefKey, final float defaultValue) {
final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT); final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT);
return (fraction != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? fraction : defaultValue; return (fraction != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? fraction : defaultValue;
} }
public static int readKeyPreviewAnimationDuration(final SharedPreferences prefs, public static int readKeyPreviewAnimationDuration(final SharedPreferences prefs,
final String prefKey, final int defaultValue) { final String prefKey, final int defaultValue) {
final int milliseconds = prefs.getInt(prefKey, UNDEFINED_PREFERENCE_VALUE_INT); final int milliseconds = prefs.getInt(prefKey, UNDEFINED_PREFERENCE_VALUE_INT);
return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue; return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue;
} }
public static float readKeyboardHeight(final SharedPreferences prefs, public static float readKeyboardHeight(final SharedPreferences prefs,
final float defaultValue) { final float defaultValue) {
final float percentage = prefs.getFloat( final float percentage = prefs.getFloat(
Settings.PREF_KEYBOARD_HEIGHT_SCALE, UNDEFINED_PREFERENCE_VALUE_FLOAT); Settings.PREF_KEYBOARD_HEIGHT_SCALE, UNDEFINED_PREFERENCE_VALUE_FLOAT);
return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue; return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue;
@ -355,7 +355,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
} }
public static boolean readShowSetupWizardIcon(final SharedPreferences prefs, public static boolean readShowSetupWizardIcon(final SharedPreferences prefs,
final Context context) { final Context context) {
if (!prefs.contains(PREF_SHOW_SETUP_WIZARD_ICON)) { if (!prefs.contains(PREF_SHOW_SETUP_WIZARD_ICON)) {
final ApplicationInfo appInfo = context.getApplicationInfo(); final ApplicationInfo appInfo = context.getApplicationInfo();
final boolean isApplicationInSystemImage = final boolean isApplicationInSystemImage =

View file

@ -28,8 +28,10 @@ import org.dslul.openboard.inputmethod.compat.AppWorkaroundsUtils;
import org.dslul.openboard.inputmethod.latin.InputAttributes; import org.dslul.openboard.inputmethod.latin.InputAttributes;
import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.R;
import org.dslul.openboard.inputmethod.latin.RichInputMethodManager; import org.dslul.openboard.inputmethod.latin.RichInputMethodManager;
import org.dslul.openboard.inputmethod.latin.common.StringUtils;
import org.dslul.openboard.inputmethod.latin.utils.AsyncResultHolder; import org.dslul.openboard.inputmethod.latin.utils.AsyncResultHolder;
import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils; import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils;
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
import org.dslul.openboard.inputmethod.latin.utils.TargetPackageInfoGetterTask; import org.dslul.openboard.inputmethod.latin.utils.TargetPackageInfoGetterTask;
import java.util.Arrays; import java.util.Arrays;
@ -118,10 +120,11 @@ public class SettingsValues {
public final float mKeyPreviewDismissEndXScale; public final float mKeyPreviewDismissEndXScale;
public final float mKeyPreviewDismissEndYScale; public final float mKeyPreviewDismissEndYScale;
@Nullable public final String mAccount; @Nullable
public final String mAccount;
public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res,
@Nonnull final InputAttributes inputAttributes) { @Nonnull final InputAttributes inputAttributes) {
mLocale = res.getConfiguration().locale; mLocale = res.getConfiguration().locale;
// Get the resources // Get the resources
mDelayInMillisecondsToUpdateOldSuggestions = mDelayInMillisecondsToUpdateOldSuggestions =
@ -132,7 +135,7 @@ public class SettingsValues {
mInputAttributes = inputAttributes; mInputAttributes = inputAttributes;
// Get the settings preferences // Get the settings preferences
mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true) && ScriptUtils.scriptSupportsUppercase(mLocale.getLanguage());
mVibrateOn = Settings.readVibrationEnabled(prefs, res); mVibrateOn = Settings.readVibrationEnabled(prefs, res);
mSoundOn = Settings.readKeypressSoundEnabled(prefs, res); mSoundOn = Settings.readKeypressSoundEnabled(prefs, res);
mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res); mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res);
@ -303,13 +306,13 @@ public class SettingsValues {
} }
private static boolean readBigramPredictionEnabled(final SharedPreferences prefs, private static boolean readBigramPredictionEnabled(final SharedPreferences prefs,
final Resources res) { final Resources res) {
return prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, res.getBoolean( return prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, res.getBoolean(
R.bool.config_default_next_word_prediction)); R.bool.config_default_next_word_prediction));
} }
private static float readAutoCorrectionThreshold(final Resources res, private static float readAutoCorrectionThreshold(final Resources res,
final String currentAutoCorrectionSetting) { final String currentAutoCorrectionSetting) {
final String[] autoCorrectionThresholdValues = res.getStringArray( final String[] autoCorrectionThresholdValues = res.getStringArray(
R.array.auto_correction_threshold_values); R.array.auto_correction_threshold_values);
// When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off. // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
@ -340,7 +343,7 @@ public class SettingsValues {
} }
private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs, private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs,
final Resources res) { final Resources res) {
// Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
// {@link Settings#PREF_VOICE_INPUT_KEY}. // {@link Settings#PREF_VOICE_INPUT_KEY}.
if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {

View file

@ -16,6 +16,9 @@
package org.dslul.openboard.inputmethod.latin.utils; package org.dslul.openboard.inputmethod.latin.utils;
import androidx.collection.ArraySet;
import java.util.Locale; import java.util.Locale;
import java.util.TreeMap; import java.util.TreeMap;
@ -47,7 +50,11 @@ public class ScriptUtils {
public static final int SCRIPT_THAI = 17; public static final int SCRIPT_THAI = 17;
public static final int SCRIPT_BULGARIAN = 18; public static final int SCRIPT_BULGARIAN = 18;
public static final String LANGUAGE_GEORGIAN = "ka";
private static final TreeMap<String, Integer> mLanguageCodeToScriptCode; private static final TreeMap<String, Integer> mLanguageCodeToScriptCode;
private final static ArraySet<String> NON_UPPERCASE_SCRIPTS = new ArraySet<>();
static { static {
mLanguageCodeToScriptCode = new TreeMap<>(); mLanguageCodeToScriptCode = new TreeMap<>();
@ -70,6 +77,13 @@ public class ScriptUtils {
mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU); mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU);
mLanguageCodeToScriptCode.put("th", SCRIPT_THAI); mLanguageCodeToScriptCode.put("th", SCRIPT_THAI);
mLanguageCodeToScriptCode.put("uk", SCRIPT_CYRILLIC); mLanguageCodeToScriptCode.put("uk", SCRIPT_CYRILLIC);
NON_UPPERCASE_SCRIPTS.add(LANGUAGE_GEORGIAN);
}
public static boolean scriptSupportsUppercase(String language) {
return !NON_UPPERCASE_SCRIPTS.contains(language);
} }
/* /*
@ -82,102 +96,102 @@ public class ScriptUtils {
*/ */
public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) { public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) {
switch (scriptId) { switch (scriptId) {
case SCRIPT_ARABIC: case SCRIPT_ARABIC:
// Arabic letters can be in any of the following blocks: // Arabic letters can be in any of the following blocks:
// Arabic U+0600..U+06FF // Arabic U+0600..U+06FF
// Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF // Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF
// Arabic Extended-A U+08A0..U+08FF // Arabic Extended-A U+08A0..U+08FF
// Arabic Presentation Forms-A U+FB50..U+FDFF // Arabic Presentation Forms-A U+FB50..U+FDFF
// Arabic Presentation Forms-B U+FE70..U+FEFF // Arabic Presentation Forms-B U+FE70..U+FEFF
return (codePoint >= 0x600 && codePoint <= 0x6FF) return (codePoint >= 0x600 && codePoint <= 0x6FF)
|| (codePoint >= 0x750 && codePoint <= 0x7BF) || (codePoint >= 0x750 && codePoint <= 0x7BF)
|| (codePoint >= 0x8A0 && codePoint <= 0x8FF) || (codePoint >= 0x8A0 && codePoint <= 0x8FF)
|| (codePoint >= 0xFB50 && codePoint <= 0xFDFF) || (codePoint >= 0xFB50 && codePoint <= 0xFDFF)
|| (codePoint >= 0xFE70 && codePoint <= 0xFEFF); || (codePoint >= 0xFE70 && codePoint <= 0xFEFF);
case SCRIPT_ARMENIAN: case SCRIPT_ARMENIAN:
// Armenian letters are in the Armenian unicode block, U+0530..U+058F and // Armenian letters are in the Armenian unicode block, U+0530..U+058F and
// Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part // Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part
// of that block, which is U+FB13..U+FB17. // of that block, which is U+FB13..U+FB17.
return (codePoint >= 0x530 && codePoint <= 0x58F return (codePoint >= 0x530 && codePoint <= 0x58F
|| codePoint >= 0xFB13 && codePoint <= 0xFB17); || codePoint >= 0xFB13 && codePoint <= 0xFB17);
case SCRIPT_BENGALI: case SCRIPT_BENGALI:
// Bengali unicode block is U+0980..U+09FF // Bengali unicode block is U+0980..U+09FF
return (codePoint >= 0x980 && codePoint <= 0x9FF); return (codePoint >= 0x980 && codePoint <= 0x9FF);
case SCRIPT_BULGARIAN: case SCRIPT_BULGARIAN:
case SCRIPT_CYRILLIC: case SCRIPT_CYRILLIC:
// All Cyrillic characters are in the 400~52F block. There are some in the upper // All Cyrillic characters are in the 400~52F block. There are some in the upper
// Unicode range, but they are archaic characters that are not used in modern // Unicode range, but they are archaic characters that are not used in modern
// Russian and are not used by our dictionary. // Russian and are not used by our dictionary.
return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
case SCRIPT_DEVANAGARI: case SCRIPT_DEVANAGARI:
// Devanagari unicode block is +0900..U+097F // Devanagari unicode block is +0900..U+097F
return (codePoint >= 0x900 && codePoint <= 0x97F); return (codePoint >= 0x900 && codePoint <= 0x97F);
case SCRIPT_GEORGIAN: case SCRIPT_GEORGIAN:
// Georgian letters are in the Georgian unicode block, U+10A0..U+10FF, // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF,
// or Georgian supplement block, U+2D00..U+2D2F // or Georgian supplement block, U+2D00..U+2D2F
return (codePoint >= 0x10A0 && codePoint <= 0x10FF return (codePoint >= 0x10A0 && codePoint <= 0x10FF
|| codePoint >= 0x2D00 && codePoint <= 0x2D2F); || codePoint >= 0x2D00 && codePoint <= 0x2D2F);
case SCRIPT_GREEK: case SCRIPT_GREEK:
// Greek letters are either in the 370~3FF range (Greek & Coptic), or in the // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
// 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters. // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
// Our dictionary also contains a few words with 0xF2; it would be best to check // Our dictionary also contains a few words with 0xF2; it would be best to check
// if that's correct, but a web search does return results for these words so // if that's correct, but a web search does return results for these words so
// they are probably okay. // they are probably okay.
return (codePoint >= 0x370 && codePoint <= 0x3FF) return (codePoint >= 0x370 && codePoint <= 0x3FF)
|| (codePoint >= 0x1F00 && codePoint <= 0x1FFF) || (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
|| codePoint == 0xF2; || codePoint == 0xF2;
case SCRIPT_HEBREW: case SCRIPT_HEBREW:
// Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF, // Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF,
// or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the // or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the
// Hebrew part of that block, which is U+FB1D..U+FB4F. // Hebrew part of that block, which is U+FB1D..U+FB4F.
return (codePoint >= 0x590 && codePoint <= 0x5FF return (codePoint >= 0x590 && codePoint <= 0x5FF
|| codePoint >= 0xFB1D && codePoint <= 0xFB4F); || codePoint >= 0xFB1D && codePoint <= 0xFB4F);
case SCRIPT_KANNADA: case SCRIPT_KANNADA:
// Kannada unicode block is U+0C80..U+0CFF // Kannada unicode block is U+0C80..U+0CFF
return (codePoint >= 0xC80 && codePoint <= 0xCFF); return (codePoint >= 0xC80 && codePoint <= 0xCFF);
case SCRIPT_KHMER: case SCRIPT_KHMER:
// Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block // Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block
// is U+19E0..U+19FF // is U+19E0..U+19FF
return (codePoint >= 0x1780 && codePoint <= 0x17FF return (codePoint >= 0x1780 && codePoint <= 0x17FF
|| codePoint >= 0x19E0 && codePoint <= 0x19FF); || codePoint >= 0x19E0 && codePoint <= 0x19FF);
case SCRIPT_LAO: case SCRIPT_LAO:
// The Lao block is U+0E80..U+0EFF // The Lao block is U+0E80..U+0EFF
return (codePoint >= 0xE80 && codePoint <= 0xEFF); return (codePoint >= 0xE80 && codePoint <= 0xEFF);
case SCRIPT_LATIN: case SCRIPT_LATIN:
// Our supported latin script dictionaries (EFIGS) at the moment only include // Our supported latin script dictionaries (EFIGS) at the moment only include
// characters in the C0, C1, Latin Extended A and B, IPA extensions unicode // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
// blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
// so the below is a very efficient way to test for it. As for the 0-0x3F, it's // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
// excluded from isLetter anyway. // excluded from isLetter anyway.
return codePoint <= 0x2AF && Character.isLetter(codePoint); return codePoint <= 0x2AF && Character.isLetter(codePoint);
case SCRIPT_MALAYALAM: case SCRIPT_MALAYALAM:
// Malayalam unicode block is U+0D00..U+0D7F // Malayalam unicode block is U+0D00..U+0D7F
return (codePoint >= 0xD00 && codePoint <= 0xD7F); return (codePoint >= 0xD00 && codePoint <= 0xD7F);
case SCRIPT_MYANMAR: case SCRIPT_MYANMAR:
// Myanmar has three unicode blocks : // Myanmar has three unicode blocks :
// Myanmar U+1000..U+109F // Myanmar U+1000..U+109F
// Myanmar extended-A U+AA60..U+AA7F // Myanmar extended-A U+AA60..U+AA7F
// Myanmar extended-B U+A9E0..U+A9FF // Myanmar extended-B U+A9E0..U+A9FF
return (codePoint >= 0x1000 && codePoint <= 0x109F return (codePoint >= 0x1000 && codePoint <= 0x109F
|| codePoint >= 0xAA60 && codePoint <= 0xAA7F || codePoint >= 0xAA60 && codePoint <= 0xAA7F
|| codePoint >= 0xA9E0 && codePoint <= 0xA9FF); || codePoint >= 0xA9E0 && codePoint <= 0xA9FF);
case SCRIPT_SINHALA: case SCRIPT_SINHALA:
// Sinhala unicode block is U+0D80..U+0DFF // Sinhala unicode block is U+0D80..U+0DFF
return (codePoint >= 0xD80 && codePoint <= 0xDFF); return (codePoint >= 0xD80 && codePoint <= 0xDFF);
case SCRIPT_TAMIL: case SCRIPT_TAMIL:
// Tamil unicode block is U+0B80..U+0BFF // Tamil unicode block is U+0B80..U+0BFF
return (codePoint >= 0xB80 && codePoint <= 0xBFF); return (codePoint >= 0xB80 && codePoint <= 0xBFF);
case SCRIPT_TELUGU: case SCRIPT_TELUGU:
// Telugu unicode block is U+0C00..U+0C7F // Telugu unicode block is U+0C00..U+0C7F
return (codePoint >= 0xC00 && codePoint <= 0xC7F); return (codePoint >= 0xC00 && codePoint <= 0xC7F);
case SCRIPT_THAI: case SCRIPT_THAI:
// Thai unicode block is U+0E00..U+0E7F // Thai unicode block is U+0E00..U+0E7F
return (codePoint >= 0xE00 && codePoint <= 0xE7F); return (codePoint >= 0xE00 && codePoint <= 0xE7F);
case SCRIPT_UNKNOWN: case SCRIPT_UNKNOWN:
return true; return true;
default: default:
// Should never come here // Should never come here
throw new RuntimeException("Impossible value of script: " + scriptId); throw new RuntimeException("Impossible value of script: " + scriptId);
} }
} }