diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java index be064b164..7f3275719 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java @@ -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.R; 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.ScriptUtils; import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -104,7 +105,9 @@ public final class KeyboardLayoutSet { boolean mProximityCharsCorrectionEnabled; boolean mSupportsSplitLayout; boolean mAllowRedundantMoreKeys; - public ElementParams() {} + + public ElementParams() { + } } public static final class Params { @@ -147,7 +150,7 @@ public final class KeyboardLayoutSet { } public static int getScriptId(final Resources resources, - @Nonnull final InputMethodSubtype subtype) { + @Nonnull final InputMethodSubtype subtype) { final Integer value = sScriptIdsForSubtypes.get(subtype); if (null == value) { final int scriptId = Builder.readScriptId(resources, subtype); @@ -162,26 +165,28 @@ public final class KeyboardLayoutSet { mParams = params; } + public static final String LOCALE_GEORGIAN = "ka"; + @Nonnull public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) { final int keyboardLayoutSetElementId; switch (mParams.mMode) { - case KeyboardId.MODE_PHONE: - if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) { - keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS; - } else { - keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE; - } - break; - case KeyboardId.MODE_NUMBER: - case KeyboardId.MODE_DATE: - case KeyboardId.MODE_TIME: - case KeyboardId.MODE_DATETIME: - keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER; - break; - default: - keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId; - break; + case KeyboardId.MODE_PHONE: + if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) { + keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS; + } else { + keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE; + } + break; + case KeyboardId.MODE_NUMBER: + case KeyboardId.MODE_DATE: + case KeyboardId.MODE_TIME: + case KeyboardId.MODE_DATETIME: + keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER; + break; + default: + keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId; + break; } ElementParams elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get( @@ -197,6 +202,7 @@ public final class KeyboardLayoutSet { mParams.mIsSplitLayoutEnabled = mParams.mIsSplitLayoutEnabledByUser && elementParams.mSupportsSplitLayout; + final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams); try { return getKeyboard(elementParams, id); @@ -279,8 +285,7 @@ public final class KeyboardLayoutSet { // be locked down. // TODO: Switch to {@code UserManagerCompat.isUserUnlocked()} in the support-v4 library // when it becomes publicly available. - @UserManagerCompatUtils.LockState - final int lockState = UserManagerCompatUtils.getUserLockState(context); + @UserManagerCompatUtils.LockState final int lockState = UserManagerCompatUtils.getUserLockState(context); if (lockState == UserManagerCompatUtils.LOCK_STATE_LOCKED) { params.mNoSettingsKey = true; } @@ -295,8 +300,7 @@ public final class KeyboardLayoutSet { public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) { final boolean asciiCapable = subtype.getmSubtype().isAsciiCapable(); // TODO: Consolidate with {@link InputAttributes}. - @SuppressWarnings("deprecation") - final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions( + @SuppressWarnings("deprecation") final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions( mPackageName, FORCE_ASCII, mParams.mEditorInfo); final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii( mParams.mEditorInfo.imeOptions) @@ -361,7 +365,7 @@ public final class KeyboardLayoutSet { } 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), R.styleable.KeyboardLayoutSet_Feature); try { @@ -473,33 +477,33 @@ public final class KeyboardLayoutSet { final int variation = inputType & InputType.TYPE_MASK_VARIATION; switch (inputType & InputType.TYPE_MASK_CLASS) { - case InputType.TYPE_CLASS_NUMBER: - return KeyboardId.MODE_NUMBER; - case InputType.TYPE_CLASS_DATETIME: - switch (variation) { - case InputType.TYPE_DATETIME_VARIATION_DATE: - return KeyboardId.MODE_DATE; - case InputType.TYPE_DATETIME_VARIATION_TIME: - return KeyboardId.MODE_TIME; - default: // InputType.TYPE_DATETIME_VARIATION_NORMAL - return KeyboardId.MODE_DATETIME; - } - case InputType.TYPE_CLASS_PHONE: - return KeyboardId.MODE_PHONE; - case InputType.TYPE_CLASS_TEXT: - if (InputTypeUtils.isEmailVariation(variation)) { - return KeyboardId.MODE_EMAIL; - } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { - return KeyboardId.MODE_URL; - } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { - return KeyboardId.MODE_IM; - } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { + case InputType.TYPE_CLASS_NUMBER: + return KeyboardId.MODE_NUMBER; + case InputType.TYPE_CLASS_DATETIME: + switch (variation) { + case InputType.TYPE_DATETIME_VARIATION_DATE: + return KeyboardId.MODE_DATE; + case InputType.TYPE_DATETIME_VARIATION_TIME: + return KeyboardId.MODE_TIME; + default: // InputType.TYPE_DATETIME_VARIATION_NORMAL + return KeyboardId.MODE_DATETIME; + } + case InputType.TYPE_CLASS_PHONE: + return KeyboardId.MODE_PHONE; + case InputType.TYPE_CLASS_TEXT: + if (InputTypeUtils.isEmailVariation(variation)) { + return KeyboardId.MODE_EMAIL; + } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { + return KeyboardId.MODE_URL; + } else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { + return KeyboardId.MODE_IM; + } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { + return KeyboardId.MODE_TEXT; + } else { + return KeyboardId.MODE_TEXT; + } + default: return KeyboardId.MODE_TEXT; - } else { - return KeyboardId.MODE_TEXT; - } - default: - return KeyboardId.MODE_TEXT; } } } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java index 658214931..acfc6896f 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/common/StringUtils.java @@ -16,7 +16,10 @@ package org.dslul.openboard.inputmethod.latin.common; +import android.renderscript.Script; + import org.dslul.openboard.inputmethod.annotations.UsedForTesting; +import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils; import java.util.ArrayList; import java.util.Arrays; @@ -26,6 +29,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public final class StringUtils { + 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_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, // some of which don't have the android libraries available. + /** * Returns true if the string is null or 0-length. + * * @param str the string to be examined * @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. + /** * Returns a string containing the tokens joined by delimiters. + * * @param delimiter the delimiter - * @param tokens an array objects to be joined. Strings will be formed from - * the objects by calling object.toString(). + * @param tokens an array objects to be joined. Strings will be formed from + * the objects by calling object.toString(). */ @Nonnull public static String join(@Nonnull final CharSequence delimiter, - @Nonnull final Iterable tokens) { + @Nonnull final Iterable tokens) { final StringBuilder sb = new StringBuilder(); boolean firstTime = true; - for (final Object token: tokens) { + for (final Object token : tokens) { if (firstTime) { firstTime = false; } else { @@ -80,10 +88,12 @@ public final class StringUtils { } // 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. *

Note: In platform versions 1.1 and earlier, this method only worked well if * both the arguments were instances of String.

+ * * @param a first CharSequence to check * @param b second CharSequence to check * @return true if a and b are equal @@ -126,7 +136,7 @@ public final class StringUtils { } public static boolean containsInArray(@Nonnull final String text, - @Nonnull final String[] array) { + @Nonnull final String[] array) { for (final String element : array) { if (text.equals(element)) { return true; @@ -144,7 +154,7 @@ public final class StringUtils { private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ","; public static boolean containsInCommaSplittableText(@Nonnull final String text, - @Nullable final String extraValues) { + @Nullable final String extraValues) { if (isEmpty(extraValues)) { return false; } @@ -153,7 +163,7 @@ public final class StringUtils { @Nonnull public static String removeFromCommaSplittableTextIfExists(@Nonnull final String text, - @Nullable final String extraValues) { + @Nullable final String extraValues) { if (isEmpty(extraValues)) { return EMPTY_STRING; } @@ -172,7 +182,7 @@ public final class StringUtils { /** * Remove duplicates from an array of strings. - * + *

* This method will always keep the first occurrence of all strings at their position * in the array, removing the subsequent ones. */ @@ -199,7 +209,7 @@ public final class StringUtils { @Nonnull public static String capitalizeFirstCodePoint(@Nonnull final String s, - @Nonnull final Locale locale) { + @Nonnull final Locale locale) { if (s.length() <= 1) { return s.toUpperCase(getLocaleUsedForToTitleCase(locale)); } @@ -212,7 +222,7 @@ public final class StringUtils { @Nonnull public static String capitalizeFirstAndDowncaseRest(@Nonnull final String s, - @Nonnull final Locale locale) { + @Nonnull final Locale locale) { if (s.length() <= 1) { 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. + * * @param charSequence the source string. - * @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 startIndex the start index inside the string in java chars, inclusive. + * @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. */ @Nonnull 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(); if (length <= 0) { return EMPTY_CODEPOINTS; @@ -259,7 +270,7 @@ public final class StringUtils { /** * Copies the codepoints in a CharSequence to an int array. - * + *

* 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 * 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 * to locale while doing so. * - * @param destination the int array. + * @param destination the int array. * @param charSequence the CharSequence. - * @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 downCase if this is true, code points will be downcased before being copied. + * @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 downCase if this is true, code points will be downcased before being copied. * @return the number of copied code points. */ public static int copyCodePointsAndReturnCodePointCount(@Nonnull final int[] destination, - @Nonnull final CharSequence charSequence, final int startIndex, final int endIndex, - final boolean downCase) { + @Nonnull final CharSequence charSequence, final int startIndex, final int endIndex, + final boolean downCase) { int destIndex = 0; 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); // TODO: stop using this, as it's not aware of the locale and does not always do // the right thing. @@ -301,7 +312,7 @@ public final class StringUtils { * Construct a String from a code point array * * @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. */ @Nonnull @@ -335,7 +346,7 @@ public final class StringUtils { int capsCount = 1; int letterCount = 1; for (index = text.offsetByCodePoints(index, 1); index < len; - index = text.offsetByCodePoints(index, 1)) { + index = text.offsetByCodePoints(index, 1)) { if (1 != capsCount && letterCount != capsCount) break; final int codePoint = text.codePointAt(index); if (Character.isUpperCase(codePoint)) { @@ -381,7 +392,7 @@ public final class StringUtils { } public static boolean isIdenticalAfterCapitalizeEachWord(@Nonnull final String text, - @Nonnull final int[] sortedSeparators) { + @Nonnull final int[] sortedSeparators) { boolean needsCapsNext = true; final int len = text.length(); 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. @Nonnull 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(); boolean needsCapsNext = true; final int len = text.length(); @@ -421,7 +432,7 @@ public final class StringUtils { /** * Approximates whether the text before the cursor looks like a URL. - * + *

* 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, * 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 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. - * + *

* Note: this method is called quite often, and should be fast. - * + *

* 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. */ @@ -491,7 +502,7 @@ public final class StringUtils { /** * Examines the string and returns whether we're inside a double quote. - * + *

* 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 * 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 public static String toTitleCaseOfKeyLabel(@Nullable final String label, - @Nonnull final Locale locale) { - if (label == null) { + @Nonnull final Locale locale) { + if (label == null || !ScriptUtils.scriptSupportsUppercase(locale.getLanguage())) { return label; } + return label.toUpperCase(getLocaleUsedForToTitleCase(locale)); } @@ -661,7 +673,7 @@ public final class StringUtils { @Nonnull protected String joinStringArray(@Nonnull final String[] stringArray, - @Nullable final String delimiter) { + @Nullable final String delimiter) { if (delimiter == null) { 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). + * * @param text the text to be examined. * @return {@code true} if the last composed word contains line-breaking separator. */ diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java index 598288563..e30c52ecb 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java @@ -172,7 +172,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang } public void loadSettings(final Context context, final Locale locale, - @Nonnull final InputAttributes inputAttributes) { + @Nonnull final InputAttributes inputAttributes) { mSettingsValuesLock.lock(); mContext = context; try { @@ -204,20 +204,20 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang // Accessed from the settings interface, hence public public static boolean readKeypressSoundEnabled(final SharedPreferences prefs, - final Resources res) { + final Resources res) { return prefs.getBoolean(PREF_SOUND_ON, res.getBoolean(R.bool.config_default_sound_enabled)); } public static boolean readVibrationEnabled(final SharedPreferences prefs, - final Resources res) { + final Resources res) { final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator(); return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON, res.getBoolean(R.bool.config_default_vibration_enabled)); } public static boolean readAutoCorrectEnabled(final SharedPreferences prefs, - final Resources res) { + final Resources res) { 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, - final Resources res) { + final Resources res) { return prefs.getBoolean(PREF_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, - final Resources res) { + final Resources res) { return readFromBuildConfigIfGestureInputEnabled(res) && prefs.getBoolean(PREF_GESTURE_INPUT, true); } @@ -249,7 +249,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang } public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs, - final Resources res) { + final Resources res) { final boolean defaultKeyPreviewPopup = res.getBoolean( R.bool.config_default_key_preview_popup); if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { @@ -259,26 +259,26 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang } public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs, - final Resources res) { + final Resources res) { return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, Integer.toString(res.getInteger( R.integer.config_key_preview_linger_timeout)))); } public static String readPrefAdditionalSubtypes(final SharedPreferences prefs, - final Resources res) { + final Resources res) { final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes( res.getStringArray(R.array.predefined_subtypes)); return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes); } public static void writePrefAdditionalSubtypes(final SharedPreferences prefs, - final String prefSubtypes) { + final String prefSubtypes) { prefs.edit().putString(PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply(); } public static float readKeypressSoundVolume(final SharedPreferences prefs, - final Resources res) { + final Resources res) { final float volume = prefs.getFloat( PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT); 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, - final Resources res) { + final Resources res) { final int milliseconds = prefs.getInt( PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); 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, - final Resources res) { + final Resources res) { final int milliseconds = prefs.getInt( PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT); 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, - final String prefKey, final float defaultValue) { + final String prefKey, final float defaultValue) { final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT); return (fraction != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? fraction : defaultValue; } 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); return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue; } public static float readKeyboardHeight(final SharedPreferences prefs, - final float defaultValue) { + final float defaultValue) { final float percentage = prefs.getFloat( Settings.PREF_KEYBOARD_HEIGHT_SCALE, UNDEFINED_PREFERENCE_VALUE_FLOAT); 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, - final Context context) { + final Context context) { if (!prefs.contains(PREF_SHOW_SETUP_WIZARD_ICON)) { final ApplicationInfo appInfo = context.getApplicationInfo(); final boolean isApplicationInSystemImage = diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java index 1bf08fef6..ca6210335 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java @@ -28,8 +28,10 @@ import org.dslul.openboard.inputmethod.compat.AppWorkaroundsUtils; import org.dslul.openboard.inputmethod.latin.InputAttributes; import org.dslul.openboard.inputmethod.latin.R; 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.ResourceUtils; +import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils; import org.dslul.openboard.inputmethod.latin.utils.TargetPackageInfoGetterTask; import java.util.Arrays; @@ -118,10 +120,11 @@ public class SettingsValues { public final float mKeyPreviewDismissEndXScale; 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, - @Nonnull final InputAttributes inputAttributes) { + @Nonnull final InputAttributes inputAttributes) { mLocale = res.getConfiguration().locale; // Get the resources mDelayInMillisecondsToUpdateOldSuggestions = @@ -132,7 +135,7 @@ public class SettingsValues { mInputAttributes = inputAttributes; // 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); mSoundOn = Settings.readKeypressSoundEnabled(prefs, res); mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res); @@ -303,13 +306,13 @@ public class SettingsValues { } private static boolean readBigramPredictionEnabled(final SharedPreferences prefs, - final Resources res) { + final Resources res) { return prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, res.getBoolean( R.bool.config_default_next_word_prediction)); } private static float readAutoCorrectionThreshold(final Resources res, - final String currentAutoCorrectionSetting) { + final String currentAutoCorrectionSetting) { final String[] autoCorrectionThresholdValues = res.getStringArray( R.array.auto_correction_threshold_values); // 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, - final Resources res) { + final Resources res) { // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to // {@link Settings#PREF_VOICE_INPUT_KEY}. if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java index 942e0d4c0..56a7b5efb 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java @@ -16,6 +16,9 @@ package org.dslul.openboard.inputmethod.latin.utils; + +import androidx.collection.ArraySet; + import java.util.Locale; import java.util.TreeMap; @@ -47,7 +50,11 @@ public class ScriptUtils { public static final int SCRIPT_THAI = 17; public static final int SCRIPT_BULGARIAN = 18; + public static final String LANGUAGE_GEORGIAN = "ka"; + private static final TreeMap mLanguageCodeToScriptCode; + private final static ArraySet NON_UPPERCASE_SCRIPTS = new ArraySet<>(); + static { mLanguageCodeToScriptCode = new TreeMap<>(); @@ -70,6 +77,13 @@ public class ScriptUtils { mLanguageCodeToScriptCode.put("te", SCRIPT_TELUGU); mLanguageCodeToScriptCode.put("th", SCRIPT_THAI); 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) { switch (scriptId) { - case SCRIPT_ARABIC: - // Arabic letters can be in any of the following blocks: - // Arabic U+0600..U+06FF - // Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF - // Arabic Extended-A U+08A0..U+08FF - // Arabic Presentation Forms-A U+FB50..U+FDFF - // Arabic Presentation Forms-B U+FE70..U+FEFF - return (codePoint >= 0x600 && codePoint <= 0x6FF) - || (codePoint >= 0x750 && codePoint <= 0x7BF) - || (codePoint >= 0x8A0 && codePoint <= 0x8FF) - || (codePoint >= 0xFB50 && codePoint <= 0xFDFF) - || (codePoint >= 0xFE70 && codePoint <= 0xFEFF); - case SCRIPT_ARMENIAN: - // 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 - // of that block, which is U+FB13..U+FB17. - return (codePoint >= 0x530 && codePoint <= 0x58F - || codePoint >= 0xFB13 && codePoint <= 0xFB17); - case SCRIPT_BENGALI: - // Bengali unicode block is U+0980..U+09FF - return (codePoint >= 0x980 && codePoint <= 0x9FF); - case SCRIPT_BULGARIAN: - case SCRIPT_CYRILLIC: - // 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 - // Russian and are not used by our dictionary. - return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); - case SCRIPT_DEVANAGARI: - // Devanagari unicode block is +0900..U+097F - return (codePoint >= 0x900 && codePoint <= 0x97F); - case SCRIPT_GEORGIAN: - // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF, - // or Georgian supplement block, U+2D00..U+2D2F - return (codePoint >= 0x10A0 && codePoint <= 0x10FF - || codePoint >= 0x2D00 && codePoint <= 0x2D2F); - case SCRIPT_GREEK: - // 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. - // 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 - // they are probably okay. - return (codePoint >= 0x370 && codePoint <= 0x3FF) - || (codePoint >= 0x1F00 && codePoint <= 0x1FFF) - || codePoint == 0xF2; - case SCRIPT_HEBREW: - // 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 - // Hebrew part of that block, which is U+FB1D..U+FB4F. - return (codePoint >= 0x590 && codePoint <= 0x5FF - || codePoint >= 0xFB1D && codePoint <= 0xFB4F); - case SCRIPT_KANNADA: - // Kannada unicode block is U+0C80..U+0CFF - return (codePoint >= 0xC80 && codePoint <= 0xCFF); - case SCRIPT_KHMER: - // Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block - // is U+19E0..U+19FF - return (codePoint >= 0x1780 && codePoint <= 0x17FF - || codePoint >= 0x19E0 && codePoint <= 0x19FF); - case SCRIPT_LAO: - // The Lao block is U+0E80..U+0EFF - return (codePoint >= 0xE80 && codePoint <= 0xEFF); - case SCRIPT_LATIN: - // Our supported latin script dictionaries (EFIGS) at the moment only include - // 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, - // so the below is a very efficient way to test for it. As for the 0-0x3F, it's - // excluded from isLetter anyway. - return codePoint <= 0x2AF && Character.isLetter(codePoint); - case SCRIPT_MALAYALAM: - // Malayalam unicode block is U+0D00..U+0D7F - return (codePoint >= 0xD00 && codePoint <= 0xD7F); - case SCRIPT_MYANMAR: - // Myanmar has three unicode blocks : - // Myanmar U+1000..U+109F - // Myanmar extended-A U+AA60..U+AA7F - // Myanmar extended-B U+A9E0..U+A9FF - return (codePoint >= 0x1000 && codePoint <= 0x109F - || codePoint >= 0xAA60 && codePoint <= 0xAA7F - || codePoint >= 0xA9E0 && codePoint <= 0xA9FF); - case SCRIPT_SINHALA: - // Sinhala unicode block is U+0D80..U+0DFF - return (codePoint >= 0xD80 && codePoint <= 0xDFF); - case SCRIPT_TAMIL: - // Tamil unicode block is U+0B80..U+0BFF - return (codePoint >= 0xB80 && codePoint <= 0xBFF); - case SCRIPT_TELUGU: - // Telugu unicode block is U+0C00..U+0C7F - return (codePoint >= 0xC00 && codePoint <= 0xC7F); - case SCRIPT_THAI: - // Thai unicode block is U+0E00..U+0E7F - return (codePoint >= 0xE00 && codePoint <= 0xE7F); - case SCRIPT_UNKNOWN: - return true; - default: - // Should never come here - throw new RuntimeException("Impossible value of script: " + scriptId); + case SCRIPT_ARABIC: + // Arabic letters can be in any of the following blocks: + // Arabic U+0600..U+06FF + // Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF + // Arabic Extended-A U+08A0..U+08FF + // Arabic Presentation Forms-A U+FB50..U+FDFF + // Arabic Presentation Forms-B U+FE70..U+FEFF + return (codePoint >= 0x600 && codePoint <= 0x6FF) + || (codePoint >= 0x750 && codePoint <= 0x7BF) + || (codePoint >= 0x8A0 && codePoint <= 0x8FF) + || (codePoint >= 0xFB50 && codePoint <= 0xFDFF) + || (codePoint >= 0xFE70 && codePoint <= 0xFEFF); + case SCRIPT_ARMENIAN: + // 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 + // of that block, which is U+FB13..U+FB17. + return (codePoint >= 0x530 && codePoint <= 0x58F + || codePoint >= 0xFB13 && codePoint <= 0xFB17); + case SCRIPT_BENGALI: + // Bengali unicode block is U+0980..U+09FF + return (codePoint >= 0x980 && codePoint <= 0x9FF); + case SCRIPT_BULGARIAN: + case SCRIPT_CYRILLIC: + // 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 + // Russian and are not used by our dictionary. + return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); + case SCRIPT_DEVANAGARI: + // Devanagari unicode block is +0900..U+097F + return (codePoint >= 0x900 && codePoint <= 0x97F); + case SCRIPT_GEORGIAN: + // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF, + // or Georgian supplement block, U+2D00..U+2D2F + return (codePoint >= 0x10A0 && codePoint <= 0x10FF + || codePoint >= 0x2D00 && codePoint <= 0x2D2F); + case SCRIPT_GREEK: + // 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. + // 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 + // they are probably okay. + return (codePoint >= 0x370 && codePoint <= 0x3FF) + || (codePoint >= 0x1F00 && codePoint <= 0x1FFF) + || codePoint == 0xF2; + case SCRIPT_HEBREW: + // 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 + // Hebrew part of that block, which is U+FB1D..U+FB4F. + return (codePoint >= 0x590 && codePoint <= 0x5FF + || codePoint >= 0xFB1D && codePoint <= 0xFB4F); + case SCRIPT_KANNADA: + // Kannada unicode block is U+0C80..U+0CFF + return (codePoint >= 0xC80 && codePoint <= 0xCFF); + case SCRIPT_KHMER: + // Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block + // is U+19E0..U+19FF + return (codePoint >= 0x1780 && codePoint <= 0x17FF + || codePoint >= 0x19E0 && codePoint <= 0x19FF); + case SCRIPT_LAO: + // The Lao block is U+0E80..U+0EFF + return (codePoint >= 0xE80 && codePoint <= 0xEFF); + case SCRIPT_LATIN: + // Our supported latin script dictionaries (EFIGS) at the moment only include + // 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, + // so the below is a very efficient way to test for it. As for the 0-0x3F, it's + // excluded from isLetter anyway. + return codePoint <= 0x2AF && Character.isLetter(codePoint); + case SCRIPT_MALAYALAM: + // Malayalam unicode block is U+0D00..U+0D7F + return (codePoint >= 0xD00 && codePoint <= 0xD7F); + case SCRIPT_MYANMAR: + // Myanmar has three unicode blocks : + // Myanmar U+1000..U+109F + // Myanmar extended-A U+AA60..U+AA7F + // Myanmar extended-B U+A9E0..U+A9FF + return (codePoint >= 0x1000 && codePoint <= 0x109F + || codePoint >= 0xAA60 && codePoint <= 0xAA7F + || codePoint >= 0xA9E0 && codePoint <= 0xA9FF); + case SCRIPT_SINHALA: + // Sinhala unicode block is U+0D80..U+0DFF + return (codePoint >= 0xD80 && codePoint <= 0xDFF); + case SCRIPT_TAMIL: + // Tamil unicode block is U+0B80..U+0BFF + return (codePoint >= 0xB80 && codePoint <= 0xBFF); + case SCRIPT_TELUGU: + // Telugu unicode block is U+0C00..U+0C7F + return (codePoint >= 0xC00 && codePoint <= 0xC7F); + case SCRIPT_THAI: + // Thai unicode block is U+0E00..U+0E7F + return (codePoint >= 0xE00 && codePoint <= 0xE7F); + case SCRIPT_UNKNOWN: + return true; + default: + // Should never come here + throw new RuntimeException("Impossible value of script: " + scriptId); } }