diff options
Diffstat (limited to 'common')
11 files changed, 608 insertions, 17 deletions
diff --git a/common/Android.mk b/common/Android.mk index 085543f75..132a22358 100644 --- a/common/Android.mk +++ b/common/Android.mk @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := latinime-common LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/common/src/com/android/inputmethod/latin/common/CodePointUtils.java b/common/src/com/android/inputmethod/latin/common/CodePointUtils.java index 592da5c1f..ec59de850 100644 --- a/common/src/com/android/inputmethod/latin/common/CodePointUtils.java +++ b/common/src/com/android/inputmethod/latin/common/CodePointUtils.java @@ -20,6 +20,8 @@ import com.android.inputmethod.annotations.UsedForTesting; import java.util.Random; +import javax.annotation.Nonnull; + // Utility methods related with code points used for tests. // TODO: Figure out where this class should be. @UsedForTesting @@ -65,17 +67,23 @@ public class CodePointUtils { }; @UsedForTesting - public static int[] generateCodePointSet(final int codePointSetSize, final Random random) { + @Nonnull + public static int[] generateCodePointSet(final int codePointSetSize, + @Nonnull final Random random) { final int[] codePointSet = new int[codePointSetSize]; for (int i = codePointSet.length - 1; i >= 0; ) { final int r = Math.abs(random.nextInt()); - if (r < 0) continue; + if (r < 0) { + continue; + } // Don't insert 0~0x20, but insert any other code point. // Code points are in the range 0~0x10FFFF. final int candidateCodePoint = 0x20 + r % (Character.MAX_CODE_POINT - 0x20); // Code points between MIN_ and MAX_SURROGATE are not valid on their own. if (candidateCodePoint >= Character.MIN_SURROGATE - && candidateCodePoint <= Character.MAX_SURROGATE) continue; + && candidateCodePoint <= Character.MAX_SURROGATE) { + continue; + } codePointSet[i] = candidateCodePoint; --i; } @@ -86,8 +94,10 @@ public class CodePointUtils { * Generates a random word. */ @UsedForTesting - public static String generateWord(final Random random, final int[] codePointSet) { - StringBuilder builder = new StringBuilder(); + @Nonnull + public static String generateWord(@Nonnull final Random random, + @Nonnull final int[] codePointSet) { + final StringBuilder builder = new StringBuilder(); // 8 * 4 = 32 chars max, but we do it the following way so as to bias the random toward // longer words. This should be closer to natural language, and more importantly, it will // exercise the algorithms in dicttool much more. diff --git a/common/src/com/android/inputmethod/latin/common/CollectionUtils.java b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java new file mode 100644 index 000000000..f7ba693af --- /dev/null +++ b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 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. + */ + +package com.android.inputmethod.latin.common; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Utility methods for working with collections. + */ +public final class CollectionUtils { + private CollectionUtils() { + // This utility class is not publicly instantiable. + } + + /** + * Converts a sub-range of the given array to an ArrayList of the appropriate type. + * @param array Array to be converted. + * @param start First index inclusive to be converted. + * @param end Last index exclusive to be converted. + * @throws IllegalArgumentException if start or end are out of range or start > end. + */ + @Nonnull + public static <E> ArrayList<E> arrayAsList(@Nonnull final E[] array, final int start, + final int end) { + if (start < 0 || start > end || end > array.length) { + throw new IllegalArgumentException("Invalid start: " + start + " end: " + end + + " with array.length: " + array.length); + } + + final ArrayList<E> list = new ArrayList<>(end - start); + for (int i = start; i < end; i++) { + list.add(array[i]); + } + return list; + } + + /** + * Tests whether c contains no elements, true if c is null or c is empty. + * @param c Collection to test. + * @return Whether c contains no elements. + */ + public static boolean isNullOrEmpty(@Nullable final Collection<?> c) { + return c == null || c.isEmpty(); + } +} diff --git a/common/src/com/android/inputmethod/latin/common/ComposedData.java b/common/src/com/android/inputmethod/latin/common/ComposedData.java new file mode 100644 index 000000000..7f0966050 --- /dev/null +++ b/common/src/com/android/inputmethod/latin/common/ComposedData.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 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. + */ + +package com.android.inputmethod.latin.common; + +import javax.annotation.Nonnull; + +/** + * An immutable class that encapsulates a snapshot of word composition data. + */ +public class ComposedData { + @Nonnull + public final InputPointers mInputPointers; + public final boolean mIsBatchMode; + @Nonnull + public final String mTypedWord; + + public ComposedData(@Nonnull final InputPointers inputPointers, final boolean isBatchMode, + @Nonnull final String typedWord) { + mInputPointers = inputPointers; + mIsBatchMode = isBatchMode; + mTypedWord = typedWord; + } + + /** + * Copy the code points in the typed word to a destination array of ints. + * + * If the array is too small to hold the code points in the typed word, nothing is copied and + * -1 is returned. + * + * @param destination the array of ints. + * @return the number of copied code points. + */ + public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( + @Nonnull final int[] destination) { + // lastIndex is exclusive + final int lastIndex = mTypedWord.length() + - StringUtils.getTrailingSingleQuotesCount(mTypedWord); + if (lastIndex <= 0) { + // The string is empty or contains only single quotes. + return 0; + } + + // The following function counts the number of code points in the text range which begins + // at index 0 and extends to the character at lastIndex. + final int codePointSize = Character.codePointCount(mTypedWord, 0, lastIndex); + if (codePointSize > destination.length) { + return -1; + } + return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWord, 0, + lastIndex, true /* downCase */); + } +} diff --git a/common/src/com/android/inputmethod/latin/common/Constants.java b/common/src/com/android/inputmethod/latin/common/Constants.java index 8f4a1e50d..abc377a84 100644 --- a/common/src/com/android/inputmethod/latin/common/Constants.java +++ b/common/src/com/android/inputmethod/latin/common/Constants.java @@ -18,6 +18,8 @@ package com.android.inputmethod.latin.common; import com.android.inputmethod.annotations.UsedForTesting; +import javax.annotation.Nonnull; + public final class Constants { public static final class Color { /** @@ -259,6 +261,7 @@ public final class Constants { return code >= CODE_SPACE; } + @Nonnull public static String printableCode(final int code) { switch (code) { case CODE_SHIFT: return "shift"; @@ -286,7 +289,8 @@ public final class Constants { } } - public static String printableCodes(final int[] codes) { + @Nonnull + public static String printableCodes(@Nonnull final int[] codes) { final StringBuilder sb = new StringBuilder(); boolean addDelimiter = false; for (final int code : codes) { diff --git a/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java new file mode 100644 index 000000000..031662411 --- /dev/null +++ b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 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. + */ + +package com.android.inputmethod.latin.common; + +import javax.annotation.Nonnull; + +public final class CoordinateUtils { + private static final int INDEX_X = 0; + private static final int INDEX_Y = 1; + private static final int ELEMENT_SIZE = INDEX_Y + 1; + + private CoordinateUtils() { + // This utility class is not publicly instantiable. + } + + @Nonnull + public static int[] newInstance() { + return new int[ELEMENT_SIZE]; + } + + public static int x(@Nonnull final int[] coords) { + return coords[INDEX_X]; + } + + public static int y(@Nonnull final int[] coords) { + return coords[INDEX_Y]; + } + + public static void set(@Nonnull final int[] coords, final int x, final int y) { + coords[INDEX_X] = x; + coords[INDEX_Y] = y; + } + + public static void copy(@Nonnull final int[] destination, @Nonnull final int[] source) { + destination[INDEX_X] = source[INDEX_X]; + destination[INDEX_Y] = source[INDEX_Y]; + } + + @Nonnull + public static int[] newCoordinateArray(final int arraySize) { + return new int[ELEMENT_SIZE * arraySize]; + } + + @Nonnull + public static int[] newCoordinateArray(final int arraySize, + final int defaultX, final int defaultY) { + final int[] result = new int[ELEMENT_SIZE * arraySize]; + for (int i = 0; i < arraySize; ++i) { + setXYInArray(result, i, defaultX, defaultY); + } + return result; + } + + public static int xFromArray(@Nonnull final int[] coordsArray, final int index) { + return coordsArray[ELEMENT_SIZE * index + INDEX_X]; + } + + public static int yFromArray(@Nonnull final int[] coordsArray, final int index) { + return coordsArray[ELEMENT_SIZE * index + INDEX_Y]; + } + + @Nonnull + public static int[] coordinateFromArray(@Nonnull final int[] coordsArray, final int index) { + final int[] coords = newInstance(); + set(coords, xFromArray(coordsArray, index), yFromArray(coordsArray, index)); + return coords; + } + + public static void setXYInArray(@Nonnull final int[] coordsArray, final int index, + final int x, final int y) { + final int baseIndex = ELEMENT_SIZE * index; + coordsArray[baseIndex + INDEX_X] = x; + coordsArray[baseIndex + INDEX_Y] = y; + } + + public static void setCoordinateInArray(@Nonnull final int[] coordsArray, final int index, + @Nonnull final int[] coords) { + setXYInArray(coordsArray, index, x(coords), y(coords)); + } +} diff --git a/common/src/com/android/inputmethod/latin/common/FileUtils.java b/common/src/com/android/inputmethod/latin/common/FileUtils.java new file mode 100644 index 000000000..676845842 --- /dev/null +++ b/common/src/com/android/inputmethod/latin/common/FileUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 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. + */ + +package com.android.inputmethod.latin.common; + +import java.io.File; +import java.io.FilenameFilter; + +/** + * A simple class to help with removing directories recursively. + */ +public class FileUtils { + public static boolean deleteRecursively(final File path) { + if (path.isDirectory()) { + final File[] files = path.listFiles(); + if (files != null) { + for (final File child : files) { + deleteRecursively(child); + } + } + } + return path.delete(); + } + + public static boolean deleteFilteredFiles(final File dir, final FilenameFilter fileNameFilter) { + if (!dir.isDirectory()) { + return false; + } + final File[] files = dir.listFiles(fileNameFilter); + if (files == null) { + return false; + } + boolean hasDeletedAllFiles = true; + for (final File file : files) { + if (!deleteRecursively(file)) { + hasDeletedAllFiles = false; + } + } + return hasDeletedAllFiles; + } +} diff --git a/common/src/com/android/inputmethod/latin/common/InputPointers.java b/common/src/com/android/inputmethod/latin/common/InputPointers.java index 40131aca4..7beee1536 100644 --- a/common/src/com/android/inputmethod/latin/common/InputPointers.java +++ b/common/src/com/android/inputmethod/latin/common/InputPointers.java @@ -18,6 +18,8 @@ package com.android.inputmethod.latin.common; import com.android.inputmethod.annotations.UsedForTesting; +import javax.annotation.Nonnull; + // TODO: This class is not thread-safe. public final class InputPointers { private static final boolean DEBUG_TIME = false; @@ -28,7 +30,7 @@ public final class InputPointers { private final ResizableIntArray mPointerIds; private final ResizableIntArray mTimes; - public InputPointers(int defaultCapacity) { + public InputPointers(final int defaultCapacity) { mDefaultCapacity = defaultCapacity; mXCoordinates = new ResizableIntArray(defaultCapacity); mYCoordinates = new ResizableIntArray(defaultCapacity); @@ -51,7 +53,8 @@ public final class InputPointers { mTimes.fill(lastTime, fromIndex, fillLength); } - public void addPointerAt(int index, int x, int y, int pointerId, int time) { + public void addPointerAt(final int index, final int x, final int y, final int pointerId, + final int time) { mXCoordinates.addAt(index, x); mYCoordinates.addAt(index, y); mPointerIds.addAt(index, pointerId); @@ -62,21 +65,21 @@ public final class InputPointers { } @UsedForTesting - public void addPointer(int x, int y, int pointerId, int time) { + public void addPointer(final int x, final int y, final int pointerId, final int time) { mXCoordinates.add(x); mYCoordinates.add(y); mPointerIds.add(pointerId); mTimes.add(time); } - public void set(InputPointers ip) { + public void set(@Nonnull final InputPointers ip) { mXCoordinates.set(ip.mXCoordinates); mYCoordinates.set(ip.mYCoordinates); mPointerIds.set(ip.mPointerIds); mTimes.set(ip.mTimes); } - public void copy(InputPointers ip) { + public void copy(@Nonnull final InputPointers ip) { mXCoordinates.copy(ip.mXCoordinates); mYCoordinates.copy(ip.mYCoordinates); mPointerIds.copy(ip.mPointerIds); @@ -93,8 +96,9 @@ public final class InputPointers { * @param startPos the starting index of the data in {@code times} and etc. * @param length the number of data to be appended. */ - public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates, - ResizableIntArray yCoordinates, int startPos, int length) { + public void append(final int pointerId, @Nonnull final ResizableIntArray times, + @Nonnull final ResizableIntArray xCoordinates, + @Nonnull final ResizableIntArray yCoordinates, final int startPos, final int length) { if (length == 0) { return; } @@ -127,14 +131,17 @@ public final class InputPointers { return mXCoordinates.getLength(); } + @Nonnull public int[] getXCoordinates() { return mXCoordinates.getPrimitiveArray(); } + @Nonnull public int[] getYCoordinates() { return mYCoordinates.getPrimitiveArray(); } + @Nonnull public int[] getPointerIds() { return mPointerIds.getPrimitiveArray(); } @@ -145,6 +152,7 @@ public final class InputPointers { * @return The time each point was registered, in milliseconds, relative to the first event in * the sequence. */ + @Nonnull public int[] getTimes() { return mTimes.getPrimitiveArray(); } diff --git a/common/src/com/android/inputmethod/latin/common/LocaleUtils.java b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java new file mode 100644 index 000000000..7f2333be5 --- /dev/null +++ b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2011 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. + */ + +package com.android.inputmethod.latin.common; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A class to help with handling Locales in string form. + * + * This file has the same meaning and features (and shares all of its code) with the one with the + * same name in Latin IME. They need to be kept synchronized; for any update/bugfix to + * this file, consider also updating/fixing the version in Latin IME. + */ +public final class LocaleUtils { + private LocaleUtils() { + // Intentional empty constructor for utility class. + } + + // Locale match level constants. + // A higher level of match is guaranteed to have a higher numerical value. + // Some room is left within constants to add match cases that may arise necessary + // in the future, for example differentiating between the case where the countries + // are both present and different, and the case where one of the locales does not + // specify the countries. This difference is not needed now. + + // Nothing matches. + public static final int LOCALE_NO_MATCH = 0; + // The languages matches, but the country are different. Or, the reference locale requires a + // country and the tested locale does not have one. + public static final int LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3; + // The languages and country match, but the variants are different. Or, the reference locale + // requires a variant and the tested locale does not have one. + public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6; + // The required locale is null or empty so it will accept anything, and the tested locale + // is non-null and non-empty. + public static final int LOCALE_ANY_MATCH = 10; + // The language matches, and the tested locale specifies a country but the reference locale + // does not require one. + public static final int LOCALE_LANGUAGE_MATCH = 15; + // The language and the country match, and the tested locale specifies a variant but the + // reference locale does not require one. + public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20; + // The compared locales are fully identical. This is the best match level. + public static final int LOCALE_FULL_MATCH = 30; + + // The level at which a match is "normally" considered a locale match with standard algorithms. + // Don't use this directly, use #isMatch to test. + private static final int LOCALE_MATCH = LOCALE_ANY_MATCH; + + // Make this match the maximum match level. If this evolves to have more than 2 digits + // when written in base 10, also adjust the getMatchLevelSortedString method. + private static final int MATCH_LEVEL_MAX = 30; + + /** + * Return how well a tested locale matches a reference locale. + * + * This will check the tested locale against the reference locale and return a measure of how + * a well it matches the reference. The general idea is that the tested locale has to match + * every specified part of the required locale. A full match occur when they are equal, a + * partial match when the tested locale agrees with the reference locale but is more specific, + * and a difference when the tested locale does not comply with all requirements from the + * reference locale. + * In more detail, if the reference locale specifies at least a language and the testedLocale + * does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the + * reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH + * if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and + * tested locale agree on the language, but not on the country, + * LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country, + * and LOCALE_LANGUAGE_MATCH otherwise. + * If they agree on both the language and the country, but not on the variant, + * LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale + * specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches, + * LOCALE_FULL_MATCH is returned. + * Examples: + * en <=> en_US => LOCALE_LANGUAGE_MATCH + * en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER + * en_US_POSIX <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER + * en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH + * sp_US <=> en_US => LOCALE_NO_MATCH + * de <=> de => LOCALE_FULL_MATCH + * en_US <=> en_US => LOCALE_FULL_MATCH + * "" <=> en_US => LOCALE_ANY_MATCH + * + * @param referenceLocale the reference locale to test against. + * @param testedLocale the locale to test. + * @return a constant that measures how well the tested locale matches the reference locale. + */ + public static int getMatchLevel(final String referenceLocale, final String testedLocale) { + if (StringUtils.isEmpty(referenceLocale)) { + return StringUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH; + } + if (null == testedLocale) return LOCALE_NO_MATCH; + final String[] referenceParams = referenceLocale.split("_", 3); + final String[] testedParams = testedLocale.split("_", 3); + // By spec of String#split, [0] cannot be null and length cannot be 0. + if (!referenceParams[0].equals(testedParams[0])) return LOCALE_NO_MATCH; + switch (referenceParams.length) { + case 1: + return 1 == testedParams.length ? LOCALE_FULL_MATCH : LOCALE_LANGUAGE_MATCH; + case 2: + if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (!referenceParams[1].equals(testedParams[1])) + return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (3 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH; + return LOCALE_FULL_MATCH; + case 3: + if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (!referenceParams[1].equals(testedParams[1])) + return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (2 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER; + if (!referenceParams[2].equals(testedParams[2])) + return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER; + return LOCALE_FULL_MATCH; + } + // It should be impossible to come here + return LOCALE_NO_MATCH; + } + + /** + * Return a string that represents this match level, with better matches first. + * + * The strings are sorted in lexicographic order: a better match will always be less than + * a worse match when compared together. + */ + public static String getMatchLevelSortedString(final int matchLevel) { + // This works because the match levels are 0~99 (actually 0~30) + // Ideally this should use a number of digits equals to the 1og10 of the greater matchLevel + return String.format(Locale.ROOT, "%02d", MATCH_LEVEL_MAX - matchLevel); + } + + /** + * Find out whether a match level should be considered a match. + * + * This method takes a match level as returned by the #getMatchLevel method, and returns whether + * it should be considered a match in the usual sense with standard Locale functions. + * + * @param level the match level, as returned by getMatchLevel. + * @return whether this is a match or not. + */ + public static boolean isMatch(final int level) { + return LOCALE_MATCH <= level; + } + + private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); + + /** + * Creates a locale from a string specification. + * @param localeString a string specification of a locale, in a format of "ll_cc_variant" where + * "ll" is a language code, "cc" is a country code. + */ + @Nullable + public static Locale constructLocaleFromString(@Nullable final String localeString) { + if (localeString == null) { + return null; + } + synchronized (sLocaleCache) { + if (sLocaleCache.containsKey(localeString)) { + return sLocaleCache.get(localeString); + } + final String[] elements = localeString.split("_", 3); + final Locale locale; + if (elements.length == 1) { + locale = new Locale(elements[0] /* language */); + } else if (elements.length == 2) { + locale = new Locale(elements[0] /* language */, elements[1] /* country */); + } else { // localeParams.length == 3 + locale = new Locale(elements[0] /* language */, elements[1] /* country */, + elements[2] /* variant */); + } + sLocaleCache.put(localeString, locale); + return locale; + } + } + + // TODO: Get this information from the framework instead of maintaining here by ourselves. + private static final HashSet<String> sRtlLanguageCodes = new HashSet<>(); + static { + // List of known Right-To-Left language codes. + sRtlLanguageCodes.add("ar"); // Arabic + sRtlLanguageCodes.add("fa"); // Persian + sRtlLanguageCodes.add("iw"); // Hebrew + sRtlLanguageCodes.add("ku"); // Kurdish + sRtlLanguageCodes.add("ps"); // Pashto + sRtlLanguageCodes.add("sd"); // Sindhi + sRtlLanguageCodes.add("ug"); // Uyghur + sRtlLanguageCodes.add("ur"); // Urdu + sRtlLanguageCodes.add("yi"); // Yiddish + } + + public static boolean isRtlLanguage(@Nonnull final Locale locale) { + return sRtlLanguageCodes.contains(locale.getLanguage()); + } +} diff --git a/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java new file mode 100644 index 000000000..7ef741cc2 --- /dev/null +++ b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 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. + */ + +package com.android.inputmethod.latin.common; + +public class NativeSuggestOptions { + // Need to update suggest_options.h when you add, remove or reorder options. + private static final int IS_GESTURE = 0; + private static final int USE_FULL_EDIT_DISTANCE = 1; + private static final int BLOCK_OFFENSIVE_WORDS = 2; + private static final int SPACE_AWARE_GESTURE_ENABLED = 3; + private static final int WEIGHT_FOR_LOCALE_IN_THOUSANDS = 4; + private static final int OPTIONS_SIZE = 5; + + private final int[] mOptions; + + public NativeSuggestOptions(final int additionalFeaturesSettingsSize) { + mOptions = new int[OPTIONS_SIZE + additionalFeaturesSettingsSize]; + } + + public void setIsGesture(final boolean value) { + setBooleanOption(IS_GESTURE, value); + } + + public void setUseFullEditDistance(final boolean value) { + setBooleanOption(USE_FULL_EDIT_DISTANCE, value); + } + + public void setBlockOffensiveWords(final boolean value) { + setBooleanOption(BLOCK_OFFENSIVE_WORDS, value); + } + + public void setSpaceAwareGestureEnabled(final boolean value) { + setBooleanOption(SPACE_AWARE_GESTURE_ENABLED, value); + } + + public void setWeightForLocale(final float value) { + // We're passing this option as a fixed point value, in thousands. This is decoded in + // native code by SuggestOptions#weightForLocale(). + setIntegerOption(WEIGHT_FOR_LOCALE_IN_THOUSANDS, (int) (value * 1000)); + } + + public void setAdditionalFeaturesOptions(final int[] additionalOptions) { + if (additionalOptions == null) { + return; + } + for (int i = 0; i < additionalOptions.length; i++) { + setIntegerOption(OPTIONS_SIZE + i, additionalOptions[i]); + } + } + + public int[] getOptions() { + return mOptions; + } + + private void setBooleanOption(final int key, final boolean value) { + mOptions[key] = value ? 1 : 0; + } + + private void setIntegerOption(final int key, final int value) { + mOptions[key] = value; + } +} diff --git a/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java index ea23d8a33..340abb23e 100644 --- a/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java +++ b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java @@ -18,8 +18,11 @@ package com.android.inputmethod.latin.common; import java.util.Arrays; +import javax.annotation.Nonnull; + // TODO: This class is not thread-safe. public final class ResizableIntArray { + @Nonnull private int[] mArray; private int mLength; @@ -89,17 +92,18 @@ public final class ResizableIntArray { mLength = 0; } + @Nonnull public int[] getPrimitiveArray() { return mArray; } - public void set(final ResizableIntArray ip) { + public void set(@Nonnull final ResizableIntArray ip) { // TODO: Implement primitive array pool. mArray = ip.mArray; mLength = ip.mLength; } - public void copy(final ResizableIntArray ip) { + public void copy(@Nonnull final ResizableIntArray ip) { final int newCapacity = calculateCapacity(ip.mLength); if (newCapacity > 0) { // TODO: Implement primitive array pool. @@ -109,7 +113,7 @@ public final class ResizableIntArray { mLength = ip.mLength; } - public void append(final ResizableIntArray src, final int startPos, final int length) { + public void append(@Nonnull final ResizableIntArray src, final int startPos, final int length) { if (length == 0) { return; } |