diff options
523 files changed, 7038 insertions, 4841 deletions
diff --git a/Android.mk b/Android.mk index aa869112c..17eeba8f0 100644 --- a/Android.mk +++ b/Android.mk @@ -12,5 +12,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -subdirs := native java tests tools +subdirs := common native java tests tools include $(call all-named-subdir-makefiles, $(subdirs)) diff --git a/common/Android.mk b/common/Android.mk new file mode 100644 index 000000000..085543f75 --- /dev/null +++ b/common/Android.mk @@ -0,0 +1,28 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := latinime-common +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_STATIC_JAVA_LIBRARIES := jsr305 +LOCAL_SDK_VERSION := 21 +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Also build a host side library +include $(CLEAR_VARS) +LOCAL_MODULE := latinime-common-host +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_STATIC_JAVA_LIBRARIES := jsr305lib +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java b/common/src/com/android/inputmethod/annotations/ExternallyReferenced.java index ea5f12ce2..ea5f12ce2 100644 --- a/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java +++ b/common/src/com/android/inputmethod/annotations/ExternallyReferenced.java diff --git a/java/src/com/android/inputmethod/annotations/UsedForTesting.java b/common/src/com/android/inputmethod/annotations/UsedForTesting.java index 2ada091e4..2ada091e4 100644 --- a/java/src/com/android/inputmethod/annotations/UsedForTesting.java +++ b/common/src/com/android/inputmethod/annotations/UsedForTesting.java diff --git a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java b/common/src/com/android/inputmethod/latin/common/CodePointUtils.java index a270ee774..ec59de850 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java +++ b/common/src/com/android/inputmethod/latin/common/CodePointUtils.java @@ -14,11 +14,17 @@ * limitations under the License. */ -package com.android.inputmethod.latin.makedict; +package com.android.inputmethod.latin.common; + +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 public class CodePointUtils { private CodePointUtils() { // This utility class is not publicly instantiable. @@ -60,17 +66,24 @@ public class CodePointUtils { 0x00FF /* LATIN SMALL LETTER Y WITH DIAERESIS */ }; - public static int[] generateCodePointSet(final int codePointSetSize, final Random random) { + @UsedForTesting + @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; } @@ -80,8 +93,11 @@ public class CodePointUtils { /** * Generates a random word. */ - public static String generateWord(final Random random, final int[] codePointSet) { - StringBuilder builder = new StringBuilder(); + @UsedForTesting + @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/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/java/src/com/android/inputmethod/latin/Constants.java b/common/src/com/android/inputmethod/latin/common/Constants.java index fc7f95c7b..abc377a84 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/common/src/com/android/inputmethod/latin/common/Constants.java @@ -14,7 +14,11 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +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 { @@ -233,8 +237,8 @@ public final class Constants { /** * Special keys code. Must be negative. - * These should be aligned with {@link KeyboardCodesSet#ID_TO_NAME}, - * {@link KeyboardCodesSet#DEFAULT}, and {@link KeyboardCodesSet#RTL}. + * These should be aligned with constants in + * {@link com.android.inputmethod.keyboard.internal.KeyboardCodesSet}. */ public static final int CODE_SHIFT = -1; public static final int CODE_CAPSLOCK = -2; @@ -257,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"; @@ -284,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) { @@ -296,21 +302,31 @@ public final class Constants { return "[" + sb + "]"; } - public static final int MAX_INT_BIT_COUNT = 32; - /** * Screen metrics (a.k.a. Device form factor) constants of - * {@link R.integer#config_screen_metrics}. + * {@link com.android.inputmethod.latin.R.integer#config_screen_metrics}. */ public static final int SCREEN_METRICS_SMALL_PHONE = 0; public static final int SCREEN_METRICS_LARGE_PHONE = 1; public static final int SCREEN_METRICS_LARGE_TABLET = 2; public static final int SCREEN_METRICS_SMALL_TABLET = 3; + @UsedForTesting + public static boolean isPhone(final int screenMetrics) { + return screenMetrics == SCREEN_METRICS_SMALL_PHONE + || screenMetrics == SCREEN_METRICS_LARGE_PHONE; + } + + @UsedForTesting + public static boolean isTablet(final int screenMetrics) { + return screenMetrics == SCREEN_METRICS_SMALL_TABLET + || screenMetrics == SCREEN_METRICS_LARGE_TABLET; + } + /** * Default capacity of gesture points container. - * This constant is used by {@link BatchInputArbiter} and etc. to preallocate regions that - * contain gesture event points. + * This constant is used by {@link com.android.inputmethod.keyboard.internal.BatchInputArbiter} + * and etc. to preallocate regions that contain gesture event points. */ public static final int DEFAULT_GESTURE_POINTS_CAPACITY = 128; diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/common/src/com/android/inputmethod/latin/common/InputPointers.java index d57a881c0..7beee1536 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/common/src/com/android/inputmethod/latin/common/InputPointers.java @@ -14,18 +14,14 @@ * limitations under the License. */ -package com.android.inputmethod.latin; - -import android.util.Log; -import android.util.SparseIntArray; +package com.android.inputmethod.latin.common; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.ResizableIntArray; + +import javax.annotation.Nonnull; // TODO: This class is not thread-safe. public final class InputPointers { - private static final String TAG = InputPointers.class.getSimpleName(); private static final boolean DEBUG_TIME = false; private final int mDefaultCapacity; @@ -34,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); @@ -57,32 +53,33 @@ 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); - if (DebugFlags.DEBUG_ENABLED || DEBUG_TIME) { + if (DEBUG_TIME) { fillWithLastTimeUntil(index); } mTimes.addAt(index, time); } @UsedForTesting - 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); @@ -99,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; } @@ -133,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(); } @@ -151,12 +152,8 @@ 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() { - if (DebugFlags.DEBUG_ENABLED || DEBUG_TIME) { - if (!isValidTimeStamps()) { - throw new RuntimeException("Time stamps are invalid."); - } - } return mTimes.getPrimitiveArray(); } @@ -165,25 +162,4 @@ public final class InputPointers { return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes + " x=" + mXCoordinates + " y=" + mYCoordinates; } - - private boolean isValidTimeStamps() { - final int[] times = mTimes.getPrimitiveArray(); - final int[] pointerIds = mPointerIds.getPrimitiveArray(); - final SparseIntArray lastTimeOfPointers = new SparseIntArray(); - final int size = getPointerSize(); - for (int i = 0; i < size; ++i) { - final int pointerId = pointerIds[i]; - final int time = times[i]; - final int lastTime = lastTimeOfPointers.get(pointerId, time); - if (time < lastTime) { - // dump - for (int j = 0; j < size; ++j) { - Log.d(TAG, "--- (" + j + ") " + times[j]); - } - return false; - } - lastTimeOfPointers.put(pointerId, time); - } - return true; - } } diff --git a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java index 64c9e2cff..340abb23e 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java +++ b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java @@ -14,12 +14,15 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; +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; } diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/common/src/com/android/inputmethod/latin/common/StringUtils.java index bbcef990d..be7260308 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/common/src/com/android/inputmethod/latin/common/StringUtils.java @@ -14,27 +14,23 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; - -import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; - -import android.text.Spanned; -import android.text.TextUtils; +package com.android.inputmethod.latin.common; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; + +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 + @Nonnull private static final String EMPTY_STRING = ""; private static final char CHAR_LINE_FEED = 0X000A; @@ -49,12 +45,77 @@ public final class StringUtils { // This utility class is not publicly instantiable. } - public static int codePointCount(final CharSequence text) { - if (TextUtils.isEmpty(text)) return 0; + // 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 + */ + public static boolean isEmpty(@Nullable final CharSequence str) { + return (str == null || str.length() == 0); + } + + // 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(). + */ + @Nonnull + public static String join(@Nonnull final CharSequence delimiter, + @Nonnull final Iterable<?> tokens) { + final StringBuilder sb = new StringBuilder(); + boolean firstTime = true; + for (final Object token: tokens) { + if (firstTime) { + firstTime = false; + } else { + sb.append(delimiter); + } + sb.append(token); + } + return sb.toString(); + } + + // 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. + * <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> + * @param a first CharSequence to check + * @param b second CharSequence to check + * @return true if a and b are equal + */ + public static boolean equals(@Nullable final CharSequence a, @Nullable final CharSequence b) { + if (a == b) { + return true; + } + final int length; + if (a != null && b != null && (length = a.length()) == b.length()) { + if (a instanceof String && b instanceof String) { + return a.equals(b); + } + for (int i = 0; i < length; i++) { + if (a.charAt(i) != b.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + + public static int codePointCount(@Nullable final CharSequence text) { + if (isEmpty(text)) { + return 0; + } return Character.codePointCount(text, 0, text.length()); } - public static String newSingleCodePointString(int codePoint) { + @Nonnull + public static String newSingleCodePointString(final int codePoint) { if (Character.charCount(codePoint) == 1) { // Optimization: avoid creating a temporary array for characters that are // represented by a single char value @@ -64,9 +125,12 @@ public final class StringUtils { return new String(Character.toChars(codePoint)); } - public static boolean containsInArray(final String text, final String[] array) { + public static boolean containsInArray(@Nonnull final String text, + @Nonnull final String[] array) { for (final String element : array) { - if (text.equals(element)) return true; + if (text.equals(element)) { + return true; + } } return false; } @@ -76,19 +140,21 @@ public final class StringUtils { * Unlike CSV, Comma-Splittable Text has no escaping mechanism, so that the text can't contain * a comma character in it. */ + @Nonnull private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ","; - public static boolean containsInCommaSplittableText(final String text, - final String extraValues) { - if (TextUtils.isEmpty(extraValues)) { + public static boolean containsInCommaSplittableText(@Nonnull final String text, + @Nullable final String extraValues) { + if (isEmpty(extraValues)) { return false; } return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT)); } - public static String removeFromCommaSplittableTextIfExists(final String text, - final String extraValues) { - if (TextUtils.isEmpty(extraValues)) { + @Nonnull + public static String removeFromCommaSplittableTextIfExists(@Nonnull final String text, + @Nullable final String extraValues) { + if (isEmpty(extraValues)) { return EMPTY_STRING; } final String[] elements = extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT); @@ -101,7 +167,7 @@ public final class StringUtils { result.add(element); } } - return TextUtils.join(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT, result); + return join(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT, result); } /** @@ -110,8 +176,10 @@ public final class StringUtils { * This method will always keep the first occurrence of all strings at their position * in the array, removing the subsequent ones. */ - public static void removeDupes(final ArrayList<String> suggestions) { - if (suggestions.size() < 2) return; + public static void removeDupes(@Nonnull final ArrayList<String> suggestions) { + if (suggestions.size() < 2) { + return; + } int i = 1; // Don't cache suggestions.size(), since we may be removing items while (i < suggestions.size()) { @@ -119,7 +187,7 @@ public final class StringUtils { // Compare each suggestion with each previous suggestion for (int j = 0; j < i; j++) { final String previous = suggestions.get(j); - if (TextUtils.equals(cur, previous)) { + if (equals(cur, previous)) { suggestions.remove(i); i--; break; @@ -129,7 +197,9 @@ public final class StringUtils { } } - public static String capitalizeFirstCodePoint(final String s, final Locale locale) { + @Nonnull + public static String capitalizeFirstCodePoint(@Nonnull final String s, + @Nonnull final Locale locale) { if (s.length() <= 1) { return s.toUpperCase(locale); } @@ -139,7 +209,9 @@ public final class StringUtils { return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff); } - public static String capitalizeFirstAndDowncaseRest(final String s, final Locale locale) { + @Nonnull + public static String capitalizeFirstAndDowncaseRest(@Nonnull final String s, + @Nonnull final Locale locale) { if (s.length() <= 1) { return s.toUpperCase(locale); } @@ -155,12 +227,14 @@ public final class StringUtils { return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale); } - private static final int[] EMPTY_CODEPOINTS = {}; - - public static int[] toCodePointArray(final CharSequence charSequence) { + @Nonnull + public static int[] toCodePointArray(@Nonnull final CharSequence charSequence) { return toCodePointArray(charSequence, 0, charSequence.length()); } + @Nonnull + private static final int[] EMPTY_CODEPOINTS = {}; + /** * Converts a range of a string to an array of code points. * @param charSequence the source string. @@ -168,7 +242,8 @@ public final class StringUtils { * @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. */ - public static int[] toCodePointArray(final CharSequence charSequence, + @Nonnull + public static int[] toCodePointArray(@Nonnull final CharSequence charSequence, final int startIndex, final int endIndex) { final int length = charSequence.length(); if (length <= 0) { @@ -199,8 +274,8 @@ public final class StringUtils { * @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(final int[] destination, - final CharSequence charSequence, final int startIndex, final int endIndex, + public static int copyCodePointsAndReturnCodePointCount(@Nonnull final int[] destination, + @Nonnull final CharSequence charSequence, final int startIndex, final int endIndex, final boolean downCase) { int destIndex = 0; for (int index = startIndex; index < endIndex; @@ -214,7 +289,8 @@ public final class StringUtils { return destIndex; } - public static int[] toSortedCodePointArray(final String string) { + @Nonnull + public static int[] toSortedCodePointArray(@Nonnull final String string) { final int[] codePoints = toCodePointArray(string); Arrays.sort(codePoints); return codePoints; @@ -227,7 +303,9 @@ public final class StringUtils { * shorter than the array length. * @return a string constructed from the code point array. */ - public static String getStringFromNullTerminatedCodePointArray(final int[] codePoints) { + @Nonnull + public static String getStringFromNullTerminatedCodePointArray( + @Nonnull final int[] codePoints) { int stringLength = codePoints.length; for (int i = 0; i < codePoints.length; i++) { if (codePoints[i] == 0) { @@ -239,7 +317,7 @@ public final class StringUtils { } // This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE. - public static int getCapitalizationType(final String text) { + public static int getCapitalizationType(@Nonnull final String text) { // If the first char is not uppercase, then the word is either all lower case or // camel case, and in either case we return CAPITALIZE_NONE. final int len = text.length(); @@ -275,7 +353,7 @@ public final class StringUtils { return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE); } - public static boolean isIdenticalAfterUpcase(final String text) { + public static boolean isIdenticalAfterUpcase(@Nonnull final String text) { final int length = text.length(); int i = 0; while (i < length) { @@ -288,7 +366,7 @@ public final class StringUtils { return true; } - public static boolean isIdenticalAfterDowncase(final String text) { + public static boolean isIdenticalAfterDowncase(@Nonnull final String text) { final int length = text.length(); int i = 0; while (i < length) { @@ -301,8 +379,8 @@ public final class StringUtils { return true; } - public static boolean isIdenticalAfterCapitalizeEachWord(final String text, - final int[] sortedSeparators) { + public static boolean isIdenticalAfterCapitalizeEachWord(@Nonnull final String text, + @Nonnull final int[] sortedSeparators) { boolean needsCapsNext = true; final int len = text.length(); for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) { @@ -321,8 +399,9 @@ public final class StringUtils { // TODO: like capitalizeFirst*, this does not work perfectly for Dutch because of the IJ digraph // which should be capitalized together in *some* cases. - public static String capitalizeEachWord(final String text, final int[] sortedSeparators, - final Locale locale) { + @Nonnull + public static String capitalizeEachWord(@Nonnull final String text, + @Nonnull final int[] sortedSeparators, @Nonnull final Locale locale) { final StringBuilder builder = new StringBuilder(); boolean needsCapsNext = true; final int len = text.length(); @@ -356,9 +435,11 @@ public final class StringUtils { * 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. */ - public static boolean lastPartLooksLikeURL(final CharSequence text) { + public static boolean lastPartLooksLikeURL(@Nonnull final CharSequence text) { int i = text.length(); - if (0 == i) return false; + if (0 == i) { + return false; + } int wCount = 0; int slashCount = 0; boolean hasSlash = false; @@ -395,11 +476,17 @@ public final class StringUtils { } // End of the text run. // If it starts with www and includes a period, then it looks like a URL. - if (wCount >= 3 && hasPeriod) return true; + if (wCount >= 3 && hasPeriod) { + return true; + } // If it starts with a slash, and the code point before is whitespace, it looks like an URL. - if (1 == slashCount && (0 == i || Character.isWhitespace(codePoint))) return true; + if (1 == slashCount && (0 == i || Character.isWhitespace(codePoint))) { + return true; + } // If it has both a period and a slash, it looks like an URL. - if (hasPeriod && hasSlash) return true; + if (hasPeriod && hasSlash) { + return true; + } // Otherwise, it doesn't look like an URL. return false; } @@ -420,18 +507,24 @@ public final class StringUtils { * @param text the text to examine. * @return whether we're inside a double quote. */ - public static boolean isInsideDoubleQuoteOrAfterDigit(final CharSequence text) { + public static boolean isInsideDoubleQuoteOrAfterDigit(@Nonnull final CharSequence text) { int i = text.length(); - if (0 == i) return false; + if (0 == i) { + return false; + } int codePoint = Character.codePointBefore(text, i); - if (Character.isDigit(codePoint)) return true; + if (Character.isDigit(codePoint)) { + return true; + } int prevCodePoint = 0; while (i > 0) { codePoint = Character.codePointBefore(text, i); if (Constants.CODE_DOUBLE_QUOTE == codePoint) { // If we see a double quote followed by whitespace, then that // was a closing quote. - if (Character.isWhitespace(prevCodePoint)) return false; + if (Character.isWhitespace(prevCodePoint)) { + return false; + } } if (Character.isWhitespace(codePoint) && Constants.CODE_DOUBLE_QUOTE == prevCodePoint) { // If we see a double quote preceded by whitespace, then that @@ -446,7 +539,7 @@ public final class StringUtils { return Constants.CODE_DOUBLE_QUOTE == codePoint; } - public static boolean isEmptyStringOrWhiteSpaces(final String s) { + public static boolean isEmptyStringOrWhiteSpaces(@Nonnull final String s) { final int N = codePointCount(s); for (int i = 0; i < N; ++i) { if (!Character.isWhitespace(s.codePointAt(i))) { @@ -457,12 +550,13 @@ public final class StringUtils { } @UsedForTesting - public static String byteArrayToHexString(final byte[] bytes) { + @Nonnull + public static String byteArrayToHexString(@Nullable final byte[] bytes) { if (bytes == null || bytes.length == 0) { return EMPTY_STRING; } final StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { + for (final byte b : bytes) { sb.append(String.format("%02x", b & 0xff)); } return sb.toString(); @@ -472,8 +566,9 @@ public final class StringUtils { * Convert hex string to byte array. The string length must be an even number. */ @UsedForTesting - public static byte[] hexStringToByteArray(final String hexString) { - if (TextUtils.isEmpty(hexString)) { + @Nullable + public static byte[] hexStringToByteArray(@Nullable final String hexString) { + if (isEmpty(hexString)) { return null; } final int N = hexString.length(); @@ -489,23 +584,28 @@ public final class StringUtils { return bytes; } - public static String toUpperCaseOfStringForLocale(final String text, - final boolean needsToUpperCase, final Locale locale) { - if (text == null || !needsToUpperCase) return text; + @Nullable + public static String toUpperCaseOfStringForLocale(@Nullable final String text, + final boolean needsToUpperCase, @Nonnull final Locale locale) { + if (text == null || !needsToUpperCase) { + return text; + } return text.toUpperCase(locale); } public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase, - final Locale locale) { - if (!Constants.isLetterCode(code) || !needsToUpperCase) return code; + @Nonnull final Locale locale) { + if (!Constants.isLetterCode(code) || !needsToUpperCase) { + return code; + } final String text = newSingleCodePointString(code); final String casedText = toUpperCaseOfStringForLocale( text, needsToUpperCase, locale); return codePointCount(casedText) == 1 - ? casedText.codePointAt(0) : CODE_UNSPECIFIED; + ? casedText.codePointAt(0) : Constants.CODE_UNSPECIFIED; } - public static int getTrailingSingleQuotesCount(final CharSequence charSequence) { + public static int getTrailingSingleQuotesCount(@Nonnull final CharSequence charSequence) { final int lastIndex = charSequence.length() - 1; int i = lastIndex; while (i >= 0 && charSequence.charAt(i) == Constants.CODE_SINGLE_QUOTE) { @@ -514,72 +614,36 @@ public final class StringUtils { return lastIndex - i; } - /** - * Splits the given {@code charSequence} with at occurrences of the given {@code regex}. - * <p> - * This is equivalent to - * {@code charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0)} - * except that the spans are preserved in the result array. - * </p> - * @param input the character sequence to be split. - * @param regex the regex pattern to be used as the separator. - * @param preserveTrailingEmptySegments {@code true} to preserve the trailing empty - * segments. Otherwise, trailing empty segments will be removed before being returned. - * @return the array which contains the result. All the spans in the {@param input} is - * preserved. - */ - @UsedForTesting - public static CharSequence[] split(final CharSequence charSequence, final String regex, - final boolean preserveTrailingEmptySegments) { - // A short-cut for non-spanned strings. - if (!(charSequence instanceof Spanned)) { - // -1 means that trailing empty segments will be preserved. - return charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0); - } - - // Hereafter, emulate String.split for CharSequence. - final ArrayList<CharSequence> sequences = new ArrayList<>(); - final Matcher matcher = Pattern.compile(regex).matcher(charSequence); - int nextStart = 0; - boolean matched = false; - while (matcher.find()) { - sequences.add(charSequence.subSequence(nextStart, matcher.start())); - nextStart = matcher.end(); - matched = true; - } - if (!matched) { - // never matched. preserveTrailingEmptySegments is ignored in this case. - return new CharSequence[] { charSequence }; - } - sequences.add(charSequence.subSequence(nextStart, charSequence.length())); - if (!preserveTrailingEmptySegments) { - for (int i = sequences.size() - 1; i >= 0; --i) { - if (!TextUtils.isEmpty(sequences.get(i))) { - break; - } - sequences.remove(i); - } - } - return sequences.toArray(new CharSequence[sequences.size()]); - } - @UsedForTesting public static class Stringizer<E> { - public String stringize(final E element) { - return element != null ? element.toString() : "null"; + @Nonnull + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + @UsedForTesting + @Nonnull + public String stringize(@Nullable final E element) { + if (element == null) { + return "null"; + } + return element.toString(); } @UsedForTesting - public final String join(final E[] array) { + @Nonnull + public final String join(@Nullable final E[] array) { return joinStringArray(toStringArray(array), null /* delimiter */); } @UsedForTesting - public final String join(final E[] array, final String delimiter) { + public final String join(@Nullable final E[] array, @Nullable final String delimiter) { return joinStringArray(toStringArray(array), delimiter); } - protected String[] toStringArray(final E[] array) { + @Nonnull + protected String[] toStringArray(@Nullable final E[] array) { + if (array == null) { + return EMPTY_STRING_ARRAY; + } final String[] stringArray = new String[array.length]; for (int index = 0; index < array.length; index++) { stringArray[index] = stringize(array[index]); @@ -587,10 +651,9 @@ public final class StringUtils { return stringArray; } - protected String joinStringArray(final String[] stringArray, final String delimiter) { - if (stringArray == null) { - return "null"; - } + @Nonnull + protected String joinStringArray(@Nonnull final String[] stringArray, + @Nullable final String delimiter) { if (delimiter == null) { return Arrays.toString(stringArray); } @@ -608,9 +671,8 @@ public final class StringUtils { * @param text the text to be examined. * @return {@code true} if the last composed word contains line-breaking separator. */ - @UsedForTesting - public static boolean hasLineBreakCharacter(final String text) { - if (TextUtils.isEmpty(text)) { + public static boolean hasLineBreakCharacter(@Nullable final String text) { + if (isEmpty(text)) { return false; } for (int i = text.length() - 1; i >= 0; --i) { diff --git a/dictionaries/de_wordlist.combined.gz b/dictionaries/de_wordlist.combined.gz Binary files differindex 803211c01..92c95540c 100644 --- a/dictionaries/de_wordlist.combined.gz +++ b/dictionaries/de_wordlist.combined.gz diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz Binary files differindex 1fa9b85ea..217660fc4 100644 --- a/dictionaries/en_GB_wordlist.combined.gz +++ b/dictionaries/en_GB_wordlist.combined.gz diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz Binary files differindex 2e039ff05..8aed9c5e0 100644 --- a/dictionaries/en_US_wordlist.combined.gz +++ b/dictionaries/en_US_wordlist.combined.gz diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz Binary files differindex e845346d6..7fe6618cf 100644 --- a/dictionaries/en_wordlist.combined.gz +++ b/dictionaries/en_wordlist.combined.gz diff --git a/dictionaries/es_wordlist.combined.gz b/dictionaries/es_wordlist.combined.gz Binary files differindex 3391e64b4..71e7309fc 100644 --- a/dictionaries/es_wordlist.combined.gz +++ b/dictionaries/es_wordlist.combined.gz diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz Binary files differindex 1b9fd73f9..afe44a6d9 100644 --- a/dictionaries/fr_wordlist.combined.gz +++ b/dictionaries/fr_wordlist.combined.gz diff --git a/dictionaries/it_wordlist.combined.gz b/dictionaries/it_wordlist.combined.gz Binary files differindex 5a5cbdc7a..ed58a12c5 100644 --- a/dictionaries/it_wordlist.combined.gz +++ b/dictionaries/it_wordlist.combined.gz diff --git a/dictionaries/nl_wordlist.combined.gz b/dictionaries/nl_wordlist.combined.gz Binary files differindex 37ba8ab42..19c3a7ea8 100644 --- a/dictionaries/nl_wordlist.combined.gz +++ b/dictionaries/nl_wordlist.combined.gz diff --git a/dictionaries/pl_wordlist.combined.gz b/dictionaries/pl_wordlist.combined.gz Binary files differindex ba71a5581..2b84eecfd 100644 --- a/dictionaries/pl_wordlist.combined.gz +++ b/dictionaries/pl_wordlist.combined.gz diff --git a/dictionaries/pt_BR_wordlist.combined.gz b/dictionaries/pt_BR_wordlist.combined.gz Binary files differindex 02df1c1ee..7aac61e50 100644 --- a/dictionaries/pt_BR_wordlist.combined.gz +++ b/dictionaries/pt_BR_wordlist.combined.gz diff --git a/dictionaries/pt_PT_wordlist.combined.gz b/dictionaries/pt_PT_wordlist.combined.gz Binary files differindex bcd50ab03..5bf9a60e8 100644 --- a/dictionaries/pt_PT_wordlist.combined.gz +++ b/dictionaries/pt_PT_wordlist.combined.gz diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz Binary files differindex 401ad08b0..5e9266221 100644 --- a/dictionaries/ru_wordlist.combined.gz +++ b/dictionaries/ru_wordlist.combined.gz diff --git a/dictionaries/sv_wordlist.combined.gz b/dictionaries/sv_wordlist.combined.gz Binary files differindex b6ebab320..db44ae4c4 100644 --- a/dictionaries/sv_wordlist.combined.gz +++ b/dictionaries/sv_wordlist.combined.gz diff --git a/dictionaries/tr_wordlist.combined.gz b/dictionaries/tr_wordlist.combined.gz Binary files differindex 306cea184..d3c8825b9 100644 --- a/dictionaries/tr_wordlist.combined.gz +++ b/dictionaries/tr_wordlist.combined.gz diff --git a/java-overridable/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java b/java-overridable/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java index 21535e421..f5e56eb4b 100644 --- a/java-overridable/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java +++ b/java-overridable/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java @@ -18,6 +18,7 @@ package com.android.inputmethod.compat; import android.content.pm.PackageInfo; +@SuppressWarnings("unused") public class AppWorkaroundsHelper { private AppWorkaroundsHelper() { // This helper class is not publicly instantiable. diff --git a/java-overridable/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java b/java-overridable/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java index ed817658e..d8951df86 100644 --- a/java-overridable/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java +++ b/java-overridable/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java @@ -21,6 +21,7 @@ import android.content.Context; /** * Helper to get the metadata URI from its base URI and the additional ID, if any. */ +@SuppressWarnings("unused") public class MetadataUriGetter { private MetadataUriGetter() { // This helper class is not instantiable. diff --git a/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java b/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java index e07a9f358..dcc64a223 100644 --- a/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java @@ -40,6 +40,7 @@ public class LoginAccountUtils { * @return an array of accounts. Empty (never null) if no accounts are available for login. */ @Nonnull + @SuppressWarnings("unused") public static String[] getAccountsForLogin(final Context context) { return new String[0]; } diff --git a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java index 99b958952..f80625644 100644 --- a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java +++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java @@ -24,7 +24,8 @@ public final class ProductionFlags { public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false; /** - * Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}. + * Include all suggestions from all dictionaries in + * {@link com.android.inputmethod.latin.SuggestedWords#mRawSuggestions}. */ public static final boolean INCLUDE_RAW_SUGGESTIONS = false; diff --git a/java-overridable/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java b/java-overridable/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java index 7dc120e06..fe1d846d8 100644 --- a/java-overridable/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java +++ b/java-overridable/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java @@ -20,6 +20,7 @@ import android.content.Context; import com.android.inputmethod.latin.DictionaryFacilitator; +@SuppressWarnings("unused") public class ContextualDictionaryUpdater { public ContextualDictionaryUpdater(final Context context, final DictionaryFacilitator dictionaryFacilitator, diff --git a/java-overridable/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java b/java-overridable/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java index 8b66cff53..64bace35a 100644 --- a/java-overridable/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java +++ b/java-overridable/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java @@ -20,6 +20,7 @@ import android.content.Context; import com.android.inputmethod.latin.DictionaryFacilitator; +@SuppressWarnings("unused") public class PersonalizationDictionaryUpdater { final Context mContext; final DictionaryFacilitator mDictionaryFacilitator; diff --git a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java index 1dfaf259e..bd54238f8 100644 --- a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java @@ -29,6 +29,7 @@ import javax.annotation.Nonnull; /** * Utility class for managing additional features settings. */ +@SuppressWarnings("unused") public class AdditionalFeaturesSettingUtils { public static final int ADDITIONAL_FEATURES_SETTINGS_SIZE = 0; diff --git a/java-overridable/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java b/java-overridable/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java index 672d6d1a1..4a44aaa94 100644 --- a/java-overridable/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java +++ b/java-overridable/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java @@ -17,9 +17,10 @@ package com.android.inputmethod.latin.touchinputconsumer; import android.view.inputmethod.EditorInfo; + import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer; import java.util.List; @@ -31,6 +32,7 @@ import java.util.Locale; * The methods of this class should only be called from a single thread, e.g., * the UI Thread. */ +@SuppressWarnings("unused") public class GestureConsumer { public static final GestureConsumer NULL_GESTURE_CONSUMER = new GestureConsumer(); diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/FeedbackUtils.java b/java-overridable/src/com/android/inputmethod/latin/utils/FeedbackUtils.java index 0aed41ee4..67de8ba32 100644 --- a/java-overridable/src/com/android/inputmethod/latin/utils/FeedbackUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/FeedbackUtils.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.utils; import android.content.Context; import android.content.Intent; +@SuppressWarnings("unused") public class FeedbackUtils { public static boolean isHelpAndFeedbackFormSupported() { return false; diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java b/java-overridable/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java index 9ad319da6..97fb17de3 100644 --- a/java-overridable/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java @@ -23,6 +23,7 @@ import android.content.Context; /** * Helper class to get the metadata URI and the additional ID. */ +@SuppressWarnings("unused") public class MetadataFileUriGetter { private MetadataFileUriGetter() { // This helper class is not instantiable. diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java index fcaeca827..044970267 100644 --- a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java @@ -22,6 +22,7 @@ import com.android.inputmethod.latin.settings.SettingsValues; import javax.annotation.Nullable; +@SuppressWarnings("unused") public final class StatsUtils { private StatsUtils() { diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java index 138f70f4c..c99dbf6a1 100644 --- a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtilsManager.java @@ -21,6 +21,7 @@ import android.content.Context; import com.android.inputmethod.latin.settings.SettingsValues; +@SuppressWarnings("unused") public class StatsUtilsManager { private static final StatsUtilsManager sInstance = new StatsUtilsManager(); diff --git a/java/Android.mk b/java/Android.mk index 0d12c45fe..a2c5697d3 100644 --- a/java/Android.mk +++ b/java/Android.mk @@ -25,7 +25,8 @@ LOCAL_CERTIFICATE := shared LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime -LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4 jsr305 +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-common inputmethod-common android-support-v4 jsr305 latinime-common # Do not compress dictionary files to mmap dict data runtime LOCAL_AAPT_FLAGS := -0 .dict diff --git a/java/res/raw/main_de.dict b/java/res/raw/main_de.dict Binary files differindex 45b288375..c3c2cbe46 100644 --- a/java/res/raw/main_de.dict +++ b/java/res/raw/main_de.dict diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict Binary files differindex 5bbb85761..b9e5bc77b 100644 --- a/java/res/raw/main_en.dict +++ b/java/res/raw/main_en.dict diff --git a/java/res/raw/main_es.dict b/java/res/raw/main_es.dict Binary files differindex fae131850..076d5aa8f 100644 --- a/java/res/raw/main_es.dict +++ b/java/res/raw/main_es.dict diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict Binary files differindex 19532d9bf..0e8686092 100644 --- a/java/res/raw/main_fr.dict +++ b/java/res/raw/main_fr.dict diff --git a/java/res/raw/main_it.dict b/java/res/raw/main_it.dict Binary files differindex ff11b9798..609ef13b7 100644 --- a/java/res/raw/main_it.dict +++ b/java/res/raw/main_it.dict diff --git a/java/res/raw/main_pt_br.dict b/java/res/raw/main_pt_br.dict Binary files differindex 9fa50442a..c33865187 100644 --- a/java/res/raw/main_pt_br.dict +++ b/java/res/raw/main_pt_br.dict diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict Binary files differindex 76b5f805a..d0af70730 100644 --- a/java/res/raw/main_ru.dict +++ b/java/res/raw/main_ru.dict diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 5f3a0676c..f8be8f1d3 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serwies (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisioneel)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompak)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal nie (alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Dieselfde invoerstyl bestaan reeds: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Sleuteldruk se vibrasie-tydsduur"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Sleuteldruk se klankvolume"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Vertraging van sleutellangdruk"</string> <string name="button_default" msgid="3988017840431881491">"Verstek"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welkom by <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"met Gebaar-tik"</string> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 4e9c4ca7a..9beccdf18 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ሂንግሊሽ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ሰርቢያኛ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ተለምዷዊ)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (እስግ)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ምንም ቋንቋ (ፊደላት)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ፊደላት (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ፊደላት (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ተመሳሳዩ የግብዓት ቅጥ አስቀድሞ አለ፦ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"የቁልፍ ጭነት ንዝረት ርዝመት"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"የቁልፍ ጭነት ድምጽ መጠን"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"የሰሌዳ ቁልፍ ጠቅታ በመጫን መዘግየት"</string> <string name="button_default" msgid="3988017840431881491">"ነባሪ"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"እንኳን ወደ <xliff:g id="APPLICATION_NAME">%s</xliff:g> በደህና መጡ"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"በጣት ምልክት መተየብ"</string> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index b5e9c5006..da414a66d 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هنجليزية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"الصربية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (التقليدية)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (مكثفة)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"بدون لغة (أبجدية)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"الأبجدية (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"الأبجدية (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"نمط الإدخال ذاته موجود من قبل: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"مدة اهتزاز الضغط على المفاتيح"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"مستوى صوت الضغط على المفاتيح"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"تأخير الضغط الطويل للمفاتيح"</string> <string name="button_default" msgid="3988017840431881491">"الافتراضية"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"مرحبا بكم في <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"مع الكتابة بالإشارة"</string> diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml index 238ed0f53..b1a192ce5 100644 --- a/java/res/values-az-rAZ/strings.xml +++ b/java/res/values-az-rAZ/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilis (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Ənənəvi)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompakt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Dil yoxdur (Əlifba)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Əlifba (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Əlifba (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Eyni daxiletmə üslubu artıq mövcuddur: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasiyalı klikləmə müddəti"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Səsli klikləmə səsi"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavişi uzun müddət basmada gecikmə"</string> <string name="button_default" msgid="3988017840431881491">"Defolt"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> təbiqinə xoş gəlmisiniz"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Jest Yazısı ilə"</string> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index ed40074b1..5d7e6b4b1 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сръбска (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционна клавиатура)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактна)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Без език (латиница)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Същият стил на въвеждане вече съществува: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Продълж. на вибриране при натискане"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Сила на звука при натиск. на клавиш"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Забавяне при продълж. натискане"</string> <string name="button_default" msgid="3988017840431881491">"Стандартни"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Добре дошли в/ъв <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"с въвеждане чрез жест"</string> diff --git a/java/res/values-bn-rBD/strings-emoji-descriptions.xml b/java/res/values-bn-rBD/strings-emoji-descriptions.xml index 3c58621f4..4c661661d 100644 --- a/java/res/values-bn-rBD/strings-emoji-descriptions.xml +++ b/java/res/values-bn-rBD/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"কুকি"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"চকোলেট বার"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ক্যান্ডি"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ললিপপ"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"কাস্টার্ড"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"মধুর পাত্র"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"শর্টকেক"</string> diff --git a/java/res/values-bn-rBD/strings.xml b/java/res/values-bn-rBD/strings.xml index b5ab6d706..8d77be4ea 100644 --- a/java/res/values-bn-rBD/strings.xml +++ b/java/res/values-bn-rBD/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"হিংলিশ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"সার্বিয়ান (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ঐতিহ্যবাহি)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (কম্প্যাক্ট)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"কোনো ভাষা নয় (বর্ণমালা)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"বর্ণমালা (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"বর্ণমালা (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"একই ইনপুট শৈলী ইতোমধ্যে বিদ্যমান: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"কীপ্রেস কম্পন সময়কাল"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"কীপ্রেস সাউন্ড ভলিউম"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"কী প্রেসে দীর্ঘ বিলম্ব"</string> <string name="button_default" msgid="3988017840431881491">"ডিফল্ট"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> এ স্বাগতম"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"অঙ্গভঙ্গি টাইপিং এর মাধ্যমে"</string> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index e39247f21..3f0e447f9 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -78,7 +78,7 @@ <string name="voice_input_disabled_summary" msgid="8141750303464726129">"No hi ha cap mètode d\'introducció activat. Comprova la configuració d\'Idioma i introducció de text."</string> <string name="configure_input_method" msgid="373356270290742459">"Configura mètodes d\'entrada"</string> <string name="language_selection_title" msgid="3666971864764478269">"Idiomes"</string> - <string name="help_and_feedback" msgid="5328219371839879161">"Ajuda i opinió"</string> + <string name="help_and_feedback" msgid="5328219371839879161">"Ajuda i suggeriments"</string> <string name="select_language" msgid="5709487854987078367">"Idiomes"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Torna a tocar per desar"</string> <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Toca aquí per desar."</string> @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacte)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Cap idioma (alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ja existeix aquest estil d\'entrada: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durada vibració en prémer"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volum del so en prémer tecles"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Retard en mantenir premut"</string> <string name="button_default" msgid="3988017840431881491">"Predeterminat"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Et donem la benvinguda a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"amb Escriptura gestual"</string> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index f52755712..4fd0e71e1 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srbština (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradiční)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktní)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Žádný jazyk (latinka)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Tento styl zadávání již existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Délka vibrace u stisku klávesy"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Hlasitost stisknutí klávesy"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Prodleva dlouhého stisknutí"</string> <string name="button_default" msgid="3988017840431881491">"Výchozí"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Vítá vás <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s psaním gesty"</string> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 240250f56..39c51e9ea 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisk (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionelt)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Intet sprog (Alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Denne inputstil findes allerede: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationstid ved tastetryk"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Lydstyrke ved tastetryk"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Forsinket langt tastetryk"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Velkommen til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med glidende indtastning"</string> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index b59c5069c..1ced6d099 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisch (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionell)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Keine Sprache (lat. Alphabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Lat. Alphabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Lat. Alphabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Der gleiche Eingabestil ist bereits vorhanden: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationsdauer bei Tastendruck"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tonlautstärke bei Tastendruck"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Verzögerung für langes Drücken"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Willkommen bei <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"mit Bewegungseingabe"</string> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index e519e4379..d9c4a40f0 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Σερβικά (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Παραδοσιακά)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Συμπαγές)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Καμία γλώσσα (Αλφάβητο)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Αλφάβητο (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Αλφάβητο (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Το ίδιο στυλ εισόδου υπάρχει ήδη: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Διάρκεια δόνησης πατήμ. πλήκτ."</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ένταση ήχου πατήματος πλήκτρου"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Καθυστέρηση παρατεταμένου πατήματος πλήκτρου"</string> <string name="button_default" msgid="3988017840431881491">"Προεπιλογή"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Καλώς ορίσατε στο <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"με Πληκτρολόγηση με κίνηση"</string> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index f31b0713c..1778cded3 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Keypress vibration duration"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Keypress sound volume"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welcome to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"with Gesture Typing"</string> diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml index f31b0713c..1778cded3 100644 --- a/java/res/values-en-rIN/strings.xml +++ b/java/res/values-en-rIN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Keypress vibration duration"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Keypress sound volume"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welcome to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"with Gesture Typing"</string> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 79b0f922d..7b9e3ba66 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durac. vibrac. al presionar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Vol. sonido al presionar tecla"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Demora de presión prolongada"</string> <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Te damos la bienvenida a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con escritura gestual"</string> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index d099e4f4b..a49ae31e8 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duración de vibración al pulsar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volumen sonido al pulsar tecla"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Retraso de pulsación prolongada"</string> <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Te damos la bienvenida a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con escritura gestual"</string> diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml index e7939e20a..440331e84 100644 --- a/java/res/values-et-rEE/strings.xml +++ b/java/res/values-et-rEE/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi-inglise (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditsiooniline)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktne)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Keel puudub (tähestik)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Tähestik (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Tähestik (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sama sisendstiil on juba olemas: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Klahvivajutuse vibreerimise kestus"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Klahvivajutuse helitugevus"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Pika klahvivajutuse viide"</string> <string name="button_default" msgid="3988017840431881491">"Vaikeväärtus"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Tere tulemast rakendusse <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"joonistusega sisestamisega"</string> diff --git a/java/res/values-eu-rES/strings.xml b/java/res/values-eu-rES/strings.xml index 3617edd2b..23d15930c 100644 --- a/java/res/values-eu-rES/strings.xml +++ b/java/res/values-eu-rES/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglisha (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbiarra (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradizionala)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (trinkoa)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ez dago hizkuntzarik (alfabetoa)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabetoa (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabetoa (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Idazketa-estilo hori badago lehendik ere: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tekla sakatzearen dardararen iraupena"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tekla sakatzearen bolumena"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Tekla luze sakatzearen atzerapena"</string> <string name="button_default" msgid="3988017840431881491">"Lehenetsia"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Ongi etorri <xliff:g id="APPLICATION_NAME">%s</xliff:g> aplikaziora"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Keinu bidezko idazketarekin"</string> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 336c8ed15..2461da5af 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هندی انگلیسی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"صربی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (سنتی)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (فشرده)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"بدون زبان (حروف الفبا)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"حروف الفبا (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"حروف الفبا (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"سبک ورودی مشابهی در حال حاضر وجود دارد: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"طول مدت لرزش در اثر فشردن کلید"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"میزان صدای فشردن کلید"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"تأخیر فشار طولانی کلید"</string> <string name="button_default" msgid="3988017840431881491">"پیشفرض"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"به <xliff:g id="APPLICATION_NAME">%s</xliff:g> خوش آمدید"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"با ورودی اشارهای"</string> diff --git a/java/res/values-fi/strings-emoji-descriptions.xml b/java/res/values-fi/strings-emoji-descriptions.xml index ad08bbda2..72af3c229 100644 --- a/java/res/values-fi/strings-emoji-descriptions.xml +++ b/java/res/values-fi/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Pikkuleipä"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Suklaapatukka"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Karamelli"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Tikkukaramelli"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vanukas"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hunajapurkki"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakkuviipale"</string> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index d1bb592e4..0308d18d7 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -74,7 +74,7 @@ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Näytä ehdotettu sana piirron aikana"</string> <string name="gesture_space_aware" msgid="2078291600664682496">"Ilmausele"</string> <string name="gesture_space_aware_summary" msgid="4371385818348528538">"Lisää välilyöntejä eleiden aikana liukumalla välilyöntinäppäim."</string> - <string name="voice_input" msgid="3583258583521397548">"Äänisyöteavain"</string> + <string name="voice_input" msgid="3583258583521397548">"Äänisyötenäppäin"</string> <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Äänen syöttötapoja ei ole otettu käyttöön. Tarkista Kieli ja syöttötapa -asetukset."</string> <string name="configure_input_method" msgid="373356270290742459">"Määritä syöttötavat"</string> <string name="language_selection_title" msgid="3666971864764478269">"Kielet"</string> @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindienglanti (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbialainen (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (perinteinen)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tiivis)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ei kieltä (aakkoset)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Aakkoset (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Aakkoset (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Syöttötyyli on jo olemassa: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Painalluksen värinän kesto"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Näppäinpainalluksen äänenvoim."</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Pitkän painalluksen viive"</string> <string name="button_default" msgid="3988017840431881491">"Oletusarvot"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Tervetuloa käyttämään sovellusta <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ja piirtokirjoitus"</string> diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml index 98796524b..6730a7982 100644 --- a/java/res/values-fr-rCA/strings.xml +++ b/java/res/values-fr-rCA/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compact)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (alphabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Le style de saisie suivant existe déjà : <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durée vibration press. touche"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume pression de touche"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Délai appui prolongé sur touche"</string> <string name="button_default" msgid="3988017840431881491">"Par défaut"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bienvenue dans <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"avec la saisie gestuelle"</string> diff --git a/java/res/values-fr/strings-emoji-descriptions.xml b/java/res/values-fr/strings-emoji-descriptions.xml index 1f99ee3bc..b7ad706fc 100644 --- a/java/res/values-fr/strings-emoji-descriptions.xml +++ b/java/res/values-fr/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biscuit"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Barre de chocolat"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bonbon"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Sucette"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Crème anglaise"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Pot de miel"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Sablé"</string> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index a93c0efe1..3349f4a6a 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi/Anglais (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (latin)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Le style de saisie suivant existe déjà : <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durée vibration press. touche"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume son pression de touche"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Délai appui prolongé sur touche"</string> <string name="button_default" msgid="3988017840431881491">"Par défaut"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bienvenue dans <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"avec la saisie gestuelle"</string> diff --git a/java/res/values-gl-rES/strings-emoji-descriptions.xml b/java/res/values-gl-rES/strings-emoji-descriptions.xml index cdb67fa45..31eb89bb2 100644 --- a/java/res/values-gl-rES/strings-emoji-descriptions.xml +++ b/java/res/values-gl-rES/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Galleta"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Barra de chocolate"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Caramelo"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Chupa-chupa"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Crema"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Tarro de mel"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Pastel"</string> diff --git a/java/res/values-gl-rES/strings.xml b/java/res/values-gl-rES/strings.xml index ca1174364..d72bcb8b1 100644 --- a/java/res/values-gl-rES/strings.xml +++ b/java/res/values-gl-rES/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Xa existe o mesmo estilo de entrada: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duración vibración ao premer teclas"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume do son ao premer teclas"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Retraso de pulsación prolongada"</string> <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Benvido a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con escritura xestual"</string> diff --git a/java/res/values-hi/strings-config-important-notice.xml b/java/res/values-hi/strings-config-important-notice.xml index 3f22541eb..c3332bb4e 100644 --- a/java/res/values-hi/strings-config-important-notice.xml +++ b/java/res/values-hi/strings-config-important-notice.xml @@ -20,5 +20,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="use_personalized_dicts_summary" msgid="590432261305469627">"सुझावों में सुधार हेतु अपने संचार और लिखे गए डेटा से जानकारी पाएं"</string> + <string name="use_personalized_dicts_summary" msgid="590432261305469627">"सुझावों में सुधार के लिए अपने संचार और लिखे गए डेटा से जानकारी पाएं"</string> </resources> diff --git a/java/res/values-hi/strings-emoji-descriptions.xml b/java/res/values-hi/strings-emoji-descriptions.xml index 1f18e6add..df5fa1e13 100644 --- a/java/res/values-hi/strings-emoji-descriptions.xml +++ b/java/res/values-hi/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"कुकी"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"चॉकलेट बार"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"कैंडी"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"लॉलीपॉप"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"दही"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"शहद का बर्तन"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"शॉर्टकेक"</string> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 5f1ca5c65..a4f2dd39d 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपरिक)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (संक्षिप्त)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"भाषा उपलब्ध नहीं है (लैटिन वर्णाक्षर)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णाक्षर (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णाक्षर (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ऐसी ही इनपुट शैली पहले से मौजूद है: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुंजी-स्पर्श कंपन अवधि"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुंजी-स्पर्श ध्वनि आवाज़"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुंजी को देर तक दबाने का विलंब"</string> <string name="button_default" msgid="3988017840431881491">"सामान्य"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> में आपका स्वागत है"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"हावभाव लेखन के साथ"</string> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index 34449c289..e1893714e 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalni)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktna)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Nema jezika (abeceda)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abeceda (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abeceda (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Već postoji isti stil unosa: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trajanje vibracije pritiska"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Glasnoća pritiska tipke"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Odgoda dugog pritiska tipke"</string> <string name="button_default" msgid="3988017840431881491">"Zadano"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Dobro došli u aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s Pisanjem kretnjama"</string> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 72b012b12..71314b0eb 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (hindi-angol, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Szerb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hagyományos)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Nincs nyelv (ábécé)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Ábécé (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Ábécé (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ugyanez a bemenetstílus már létezik: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Gombnyomás rezgési időtartama"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Gombnyomás hangereje"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Hosszú nyomás késleltetése"</string> <string name="button_default" msgid="3988017840431881491">"Alapértelmezett"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Üdvözli a(z) <xliff:g id="APPLICATION_NAME">%s</xliff:g>!"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kézmozdulatokkal történő bevitellel"</string> diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml index 488bcc659..8d7c5c8c1 100644 --- a/java/res/values-hy-rAM/strings.xml +++ b/java/res/values-hy-rAM/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Հինգլիշ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Սերբերեն (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ավանդական)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (սեղմ)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ոչ մի լեզվով (Այբուբեն)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Այբուբեն (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Այբուբեն (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Մուտքագրման այսպիսի ոճ արդեն գոյություն ունի՝ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Սեղմման թրթռոցի տևողություն"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Սեղմման ձայնի բարձրությունը"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ստեղնի երկար սեղմման ուշացում"</string> <string name="button_default" msgid="3988017840431881491">"Լռելյայնը"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Բարի գալուստ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Ժեստային մուտքագրմամբ"</string> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index 1a67c3e5f..bd86f01b1 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Rapat)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Tidak ada bahasa (Abjad)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sudah ada gaya masukan yang sama: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durasi getar saat tekan tombol"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume suara saat tekan tombol"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Penundaan tekan lama tombol"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang di <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Ketikan Isyarat"</string> diff --git a/java/res/values-is-rIS/strings.xml b/java/res/values-is-rIS/strings.xml index 79255ee6b..8a6927af1 100644 --- a/java/res/values-is-rIS/strings.xml +++ b/java/res/values-is-rIS/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbneskt (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hefðbundið)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (lítið)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ekkert tungumál (stafróf)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Stafróf (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Stafróf (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sama innsláttaraðferð er þegar fyrir hendi: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Lengd lyklatitrings"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Styrkur lyklahljóða"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Töf áður en lykli er haldið inni"</string> <string name="button_default" msgid="3988017840431881491">"Sjálfgefið"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Velkomin(n) í <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"með bendingainnslætti"</string> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 473d71285..2bca3b40b 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbo (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradizionale)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compatto)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Nessuna lingua (alfabeto)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Esiste già uno stile di inuput uguale: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durata vibraz. pressione tasto"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume audio a pressione tasto"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ritardo pressione lunga tasti"</string> <string name="button_default" msgid="3988017840431881491">"Predefinito"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Benvenuto in <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con la Digitazione gestuale"</string> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index 317282deb..c99d5123b 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"אנגלית הודית (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"סרבית (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (מסורתית)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (קומפקטית)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ללא שפה (אלף-בית)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"אלף-בית (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"אלף-בית (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"סגנון קלט זהה כבר קיים: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"משך רטט של לחיצת מקש"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"עוצמת קול של לחיצת מקש"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"השהיית לחיצה ארוכה על מקש"</string> <string name="button_default" msgid="3988017840431881491">"ברירת מחדל"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"ברוך הבא אל <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"עם הקלדת החלקה"</string> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index d3be98be6..53ef5779f 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ヒングリッシュ(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"セルビア語(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(伝統言語)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(コンパクト)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"言語なし(アルファベット)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"アルファベット(QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"アルファベット(QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"同じ入力スタイルが既に存在します: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"キー操作バイブの振動時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"キー操作音の音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"キーの長押し時間"</string> <string name="button_default" msgid="3988017840431881491">"デフォルト"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>へようこそ"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"新しいジェスチャー入力をお試しください"</string> diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml index f614ea08a..d4457a508 100644 --- a/java/res/values-ka-rGE/strings.xml +++ b/java/res/values-ka-rGE/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ჰინგლისური (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"სერბული (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ტრადიციული)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (კომპაქტური)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ენის გარეშე (ანბანი)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ანბანი (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ანბანი (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"შეყვანის იგივე სტილი უკვე არსებობს: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"კლავიშზე დაჭერის ვიბრაციის ხანგრძლივობა"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"კლავიშზე დაჭერის ხმა"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"კლავიშზე გრძელი დაჭერის დაყოვნება"</string> <string name="button_default" msgid="3988017840431881491">"ნაგულისხმევი"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"კეთილი იყოს თქვენი მობრძანება <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ში"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ჟესტებით წერით"</string> diff --git a/java/res/values-kk-rKZ/strings.xml b/java/res/values-kk-rKZ/strings.xml index 108075a64..58f8f306e 100644 --- a/java/res/values-kk-rKZ/strings.xml +++ b/java/res/values-kk-rKZ/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Серб (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (дәстүрлі)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (шағын)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Тіл жоқ (әліпби)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Әліпби (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Әліпби (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Бірдей енгізу стилі бұрыннан бар: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Пернені басқан кездегі діріл ұзақтығы"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Пернені басқан кездегі дыбыс деңгейі"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Пернені ұзақ басу кідірісі"</string> <string name="button_default" msgid="3988017840431881491">"Әдепкі"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> қолданбасына қош келдіңіз"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Қимылмен теру арқылы"</string> diff --git a/java/res/values-km-rKH/strings-emoji-descriptions.xml b/java/res/values-km-rKH/strings-emoji-descriptions.xml index 9f1d9973e..757df50e7 100644 --- a/java/res/values-km-rKH/strings-emoji-descriptions.xml +++ b/java/res/values-km-rKH/strings-emoji-descriptions.xml @@ -25,16 +25,16 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="spoken_emoji_00A9" msgid="2859822817116803638">"សញ្ញារក្សាសិទ្ធ"</string> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"សញ្ញារក្សាសិទ្ធ"</string> <string name="spoken_emoji_00AE" msgid="7708335454134589027">"សញ្ញាចុះបញ្ជី"</string> <string name="spoken_emoji_203C" msgid="153340916701508663">"សញ្ញាឧទានពីរ"</string> - <string name="spoken_emoji_2049" msgid="4877256448299555371">"សញ្ញាឧទានសញ្ញាសួរ"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"សញ្ញាឧទានសញ្ញាសួរ"</string> <string name="spoken_emoji_2122" msgid="9188440722954720429">"សញ្ញានិក្ខិត្តសញ្ញា"</string> <string name="spoken_emoji_2139" msgid="9114342638917304327">"ប្រភពព័ត៌មាន"</string> <string name="spoken_emoji_2194" msgid="8055202727034946680">"ព្រួញឆ្វេងស្ដាំ"</string> <string name="spoken_emoji_2195" msgid="8028122253301087407">"ព្រួញឡើងលើចុះក្រោម"</string> <string name="spoken_emoji_2196" msgid="4019164898967854363">"ព្រួញទិសពាយព្យ"</string> - <string name="spoken_emoji_2197" msgid="4255723717709017801">"ព្រួញទិសឥសាន្តឦសាន្ត"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"ព្រួញទិសឥសាន្តឦសាន្ត"</string> <string name="spoken_emoji_2198" msgid="1452063451313622090">"ព្រួញទិសអាគ្នេយ៍"</string> <string name="spoken_emoji_2199" msgid="6942722693368807849">"ព្រួញទិសនិរតី"</string> <string name="spoken_emoji_21A9" msgid="5204750172335111188">"ព្រួញទៅឆ្វេងមានទំពក់"</string> @@ -45,7 +45,7 @@ <string name="spoken_emoji_23EA" msgid="2251396938087774944">"ត្រីកោណខ្មៅពីរចង្អុលទៅឆ្វេង"</string> <string name="spoken_emoji_23EB" msgid="3746885195641491865">"ត្រីកោណខ្មៅពីរចង្អុលឡើងលើ"</string> <string name="spoken_emoji_23EC" msgid="7852372752901163416">"ត្រីកោណខ្មៅពីរចង្អុលចុះក្រោម"</string> - <string name="spoken_emoji_23F0" msgid="8474219588750627870">"នាឡិការោទ៍"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"នាឡិការោទ៍"</string> <string name="spoken_emoji_23F3" msgid="166900119581024371">"កែវពិសោធន៍មានខ្សាច់ហូរ"</string> <string name="spoken_emoji_24C2" msgid="3948348737566038470">"អក្សរអឹមធំក្នុងរង្វង់"</string> <string name="spoken_emoji_25AA" msgid="7865181015100227349">"ការ៉េតូចពណ៌ខ្មៅ"</string> @@ -195,7 +195,7 @@ <string name="spoken_emoji_1F312" msgid="4458575672576125401">"ព្រះចន្ទមួយចំណិតស្ដាំ"</string> <string name="spoken_emoji_1F313" msgid="7599181787989497294">"ព្រះចន្ទពាក់កណ្ដាល"</string> <string name="spoken_emoji_1F314" msgid="4898293184964365413">"ព្រះចន្ទមួយចំណិតឆ្វេង"</string> - <string name="spoken_emoji_1F315" msgid="3218117051779496309">"ព្រះចន្ទពេញវង់"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"ព្រះចន្ទពេញវង់"</string> <string name="spoken_emoji_1F316" msgid="2061317145777689569">"ព្រះចន្ទភ្លឺមួយចំហៀង"</string> <string name="spoken_emoji_1F317" msgid="2721090687319539049">"ព្រះចន្ទភ្លឺពាក់កណ្ដាល"</string> <string name="spoken_emoji_1F318" msgid="3814091755648887570">"ព្រះចន្ទភ្លឺមួយចំណិតឆ្វេង"</string> @@ -262,7 +262,7 @@ <string name="spoken_emoji_1F365" msgid="4963815540953316307">"នំត្រីរាងមូល"</string> <string name="spoken_emoji_1F366" msgid="7862401745277049404">"ការ៉េមបំពង់"</string> <string name="spoken_emoji_1F367" msgid="7447972978281980414">"ការ៉េមកែវ"</string> - <string name="spoken_emoji_1F368" msgid="7790003146142724913">"ការ៉េម"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"ការ៉េម"</string> <string name="spoken_emoji_1F369" msgid="7383712944084857350">"ដូណាត់"</string> <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"ខូគី"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"សូកូឡា"</string> @@ -280,7 +280,7 @@ <string name="spoken_emoji_1F377" msgid="1762398562314172075">"កែវស្រា"</string> <string name="spoken_emoji_1F378" msgid="5528234560590117516">"កែវស្រាក្រឡុក"</string> <string name="spoken_emoji_1F379" msgid="790581290787943325">"ភេសជ្ជៈត្រូពិក"</string> - <string name="spoken_emoji_1F37A" msgid="391966822450619516">"កែវស្រាបៀ"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"កែវស្រាបៀ"</string> <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"ជល់កែវស្រាបៀ"</string> <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"ដបទឹកដោះគោ"</string> <string name="spoken_emoji_1F380" msgid="3487363857092458827">"ខ្សែបូ"</string> @@ -313,7 +313,7 @@ <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"កាស"</string> <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"ក្ដារលាយពណ៌វិចិត្រករ"</string> <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"មួកសម្ដែងសិល្បៈ"</string> - <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"តង់សៀក"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"តង់សៀក"</string> <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"សំបុត្រ"</string> <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"បន្ទះកណ្ដឹង"</string> <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"សម្ដែងសិល្បៈ"</string> @@ -334,10 +334,10 @@ <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"និមិត្តសញ្ញាតន្ត្រី"</string> <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"អាវកីឡាមានខ្សែឆៀង"</string> <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"រ៉ាកែត និងបាល់"</string> - <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"ជិះស្គី និងក្ដារស្គី"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"ជិះស្គី និងក្ដារស្គី"</string> <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"បាល់បោះ និងវណ្ណមូល"</string> <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"ទង់ជាតិប្រណាំងម៉ូតូ"</string> - <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"អ្នកជិះក្ដាររំអិលលើព្រិល"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"អ្នកជិះក្ដាររំអិលលើព្រិល"</string> <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"អ្នករត់"</string> <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"អ្នកជិះទូករអិលលើទឹក"</string> <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"ពានរង្វាន់"</string> @@ -354,7 +354,7 @@ <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"ធនាគារ"</string> <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"ម៉ាស៊ីនអេធីអឹម"</string> <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"សណ្ឋាគារ"</string> - <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"សណ្ឋាគារក្ដីស្រឡាញ់"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"សណ្ឋាគារក្ដីស្រឡាញ់"</string> <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"ហាងទំនិញ ២៤ម៉ោង"</string> <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"សាលារៀន"</string> <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"ហាងទំនិញធំៗ"</string> @@ -439,12 +439,12 @@ <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"មេដៃឡើងលើ"</string> <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"មេដៃចុះក្រោម"</string> <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"ទះដៃ"</string> - <string name="spoken_emoji_1F450" msgid="1012021072085157054">"លាដៃ"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"លាដៃ"</string> <string name="spoken_emoji_1F451" msgid="8257466714629051320">"មកុដ"</string> <string name="spoken_emoji_1F452" msgid="4567394011149905466">"មួកស្ត្រី"</string> <string name="spoken_emoji_1F453" msgid="5978410551173163010">"វ៉ែនតា"</string> - <string name="spoken_emoji_1F454" msgid="348469036193323252">"ក្រវ៉ាត់ករ"</string> - <string name="spoken_emoji_1F455" msgid="5665118831861433578">"អាវយឺត"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"ក្រវ៉ាត់ករ"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"អាវយឺត"</string> <string name="spoken_emoji_1F456" msgid="1890991330923356408">"ខោខោវប៊យ"</string> <string name="spoken_emoji_1F457" msgid="3904310482655702620">"សំលៀកបំពាក់"</string> <string name="spoken_emoji_1F458" msgid="5704243858031107692">"គីម៉ូណូ"</string> @@ -463,8 +463,8 @@ <string name="spoken_emoji_1F465" msgid="4461307702499679879">"គណនី"</string> <string name="spoken_emoji_1F466" msgid="1938873085514108889">"ក្មេងប្រុស"</string> <string name="spoken_emoji_1F467" msgid="8237080594860144998">"ក្មេងស្រី"</string> - <string name="spoken_emoji_1F468" msgid="6081300722526675382">"បុរស"</string> - <string name="spoken_emoji_1F469" msgid="1090140923076108158">"ស្ត្រី"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"បុរស"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"ស្ត្រី"</string> <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"គ្រួសារ"</string> <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"បុរស និងស្ត្រីកាន់ដៃគ្នា"</string> <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"បុរសពីរនាក់កាន់ដៃគ្នា"</string> @@ -490,16 +490,16 @@ <string name="spoken_emoji_1F480" msgid="3696253485164878739">"លលាដ៍ក្បាល"</string> <string name="spoken_emoji_1F481" msgid="320408708521966893">"អ្នកផ្ដល់ព័ត៌មាន"</string> <string name="spoken_emoji_1F482" msgid="3424354860245608949">"អ្នកយាម"</string> - <string name="spoken_emoji_1F483" msgid="3221113594843849083">"អ្នករាំ"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"អ្នករាំ"</string> <string name="spoken_emoji_1F484" msgid="7348014979080444885">"ក្រេមលាបបបូរមាត់"</string> <string name="spoken_emoji_1F485" msgid="6133507975565116339">"ថ្នាំលាបក្រចក"</string> <string name="spoken_emoji_1F486" msgid="9085459968247394155">"ម៉ាស្សាមុខ"</string> <string name="spoken_emoji_1F487" msgid="1479113637259592150">"កាត់សក់"</string> <string name="spoken_emoji_1F488" msgid="6922559285234100252">"ស្លាកសញ្ញាកាត់សក់"</string> <string name="spoken_emoji_1F489" msgid="8114863680950147305">"ស៊ីរ៉ាំង"</string> - <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"ថ្នាំគ្រាប់"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"ថ្នាំគ្រាប់"</string> <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"ស្នាមថើប"</string> - <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"លិខិតស្នេហា"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"លិខិតស្នេហា"</string> <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"រោទ៍"</string> <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"ត្បូងថ្ម"</string> <string name="spoken_emoji_1F48F" msgid="741593675183677907">"ថើប"</string> @@ -525,7 +525,7 @@ <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"គ្រាប់បែក"</string> <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"និមិត្តសញ្ញាដេក"</string> <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"និមិត្តសញ្ញាប៉ះទង្គិចគ្នា"</string> - <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"និមិត្តសញ្ញាស្រក់ញើស"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"និមិត្តសញ្ញាស្រក់ញើស"</string> <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"ដំណក់ទឹក"</string> <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"និមិត្តសញ្ញាដកឃ្លា"</string> <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"គំនរធូលី"</string> @@ -539,7 +539,7 @@ <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"ប្ដូររូបិយប័ណ្ណ"</string> <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"សញ្ញាដុល្លារ"</string> <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"កាតឥណទាន"</string> - <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"ក្រដាសប្រាក់ធនាគារមានសញ្ញាយ៉េន"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"ក្រដាសប្រាក់ធនាគារមានសញ្ញាយ៉េន"</string> <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"លុយដុល្លារ"</string> <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"ក្រដាសប្រាក់ធនាគារមានសញ្ញាអឺរ៉ូ"</string> <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"ក្រដាសប្រាក់ធនាគារមានសញ្ញាផោន"</string> @@ -547,7 +547,7 @@ <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"ក្រាហ្វិកនិន្នាការឡើងមានសញ្ញាយ៉េន"</string> <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"កៅអី"</string> <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"កុំព្យូទ័រផ្ទាល់ខ្លួន"</string> - <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"វ៉ាលី"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"វ៉ាលី"</string> <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"ឌីសតូច"</string> <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"ថាសទន់"</string> <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"ថាស"</string> @@ -557,7 +557,7 @@ <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"ទំព័រកោង"</string> <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"ទំព័របញ្ឈរ"</string> <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"ប្រតិទិន"</string> - <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"ហែកប្រតិទិន"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"ហែកប្រតិទិន"</string> <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"កាតរៀបតាមអក្សរ"</string> <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"ក្រាហ្វិកមាននិន្នាការឡើង"</string> <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"ក្រាហ្វិកមាននិន្នាការចុះ"</string> @@ -573,11 +573,11 @@ <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"សៀវភៅ"</string> <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"សៀវភៅមានក្របពណ៌"</string> <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"សៀវភៅបិទ"</string> - <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"សៀវភៅបើក"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"សៀវភៅបើក"</string> <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"សៀវភៅពណ៌បៃតង"</string> <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"សៀវភៅពណ៌ខៀវ"</string> <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"សៀវភៅពណ៌ទឹកក្រូច"</string> - <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"សៀវភៅ"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"សៀវភៅ"</string> <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"ស្លាកឈ្មោះ"</string> <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"ក្រដាសរមូរ"</string> <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"កំណត់ចំណាំ"</string> @@ -589,7 +589,7 @@ <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"ឧបករណ៍បំពងសំឡេង"</string> <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"ថាសចេញ"</string> <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"ថាសចូល"</string> - <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"កញ្ចប់"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"កញ្ចប់"</string> <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"និមិត្តសញ្ញាអ៊ីមែល"</string> <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"ស្រោមសំបុត្រចូល"</string> <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"ស្រោមសំបុត្រមានសញ្ញាព្រួញពីលើ"</string> @@ -626,7 +626,7 @@ <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"ដុយអគ្គិសនី"</string> <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"កែវពង្រីកចង្អុលខាងឆ្វេង"</string> <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"កែវពង្រីកចង្អុលខាងស្ដាំ"</string> - <string name="spoken_emoji_1F50F" msgid="915079280472199605">"ចាក់សោដោយប្រើប៊ិច"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"ចាក់សោដោយប្រើប៊ិច"</string> <string name="spoken_emoji_1F510" msgid="7658381761691758318">"បិទសោដោយប្រើកូនសោ"</string> <string name="spoken_emoji_1F511" msgid="262319867774655688">"សោ"</string> <string name="spoken_emoji_1F512" msgid="5628688337255115175">"ចាក់សោ"</string> @@ -645,15 +645,15 @@ <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"គ្រាប់ចុច ១០"</string> <string name="spoken_emoji_1F520" msgid="7335109890337048900">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់អក្សរឡាតាំងធំ"</string> <string name="spoken_emoji_1F521" msgid="2693185864450925778">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់អក្សរឡាតាំងតូច"</string> - <string name="spoken_emoji_1F522" msgid="8419130286280673347">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់លេខ"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់លេខ"</string> <string name="spoken_emoji_1F523" msgid="3318053476401719421">"ការបញ្ចូលនិមិត្តសញ្ញា"</string> <string name="spoken_emoji_1F524" msgid="1625073997522316331">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់អក្សរឡាតាំង"</string> <string name="spoken_emoji_1F525" msgid="4083884189172963790">"ភ្លើង"</string> <string name="spoken_emoji_1F526" msgid="2035494936742643580">"ពិលអគ្គិសនី"</string> <string name="spoken_emoji_1F527" msgid="134257142354034271">"ម៉ាឡេត"</string> <string name="spoken_emoji_1F528" msgid="700627429570609375">"ញញួរ"</string> - <string name="spoken_emoji_1F529" msgid="7480548235904988573">"ឡោស៊ី"</string> - <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"កាំបិត"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"ឡោស៊ី"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"កាំបិត"</string> <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"កាំភ្លើងខ្លី"</string> <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"មីក្រូទស្សន៍"</string> <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"កែវយឹត"</string> @@ -662,7 +662,7 @@ <string name="spoken_emoji_1F530" msgid="3572898444281774023">"និមិត្តសញ្ញាជប៉ុនសម្រាប់អ្នកចាប់ផ្ដើម"</string> <string name="spoken_emoji_1F531" msgid="5225633376450025396">"លំពែងមុខបី"</string> <string name="spoken_emoji_1F532" msgid="9169568490485180779">"ប៊ូតុងការេពណ៌ខ្មៅ"</string> - <string name="spoken_emoji_1F533" msgid="6554193837201918598">"ប៊ូតុងការ៉េពណ៌ស"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"ប៊ូតុងការ៉េពណ៌ស"</string> <string name="spoken_emoji_1F534" msgid="8339298801331865340">"រង្វង់ពណ៌ក្រហមធំ"</string> <string name="spoken_emoji_1F535" msgid="1227403104835533512">"រង្វង់ពណ៌ខៀវធំ"</string> <string name="spoken_emoji_1F536" msgid="5477372445510469331">"ពេជ្រពណ៌ទឹកក្រូចធំ"</string> @@ -745,8 +745,8 @@ <string name="spoken_emoji_1F628" msgid="8875777401624904224">"មុខភ័យខ្លាច"</string> <string name="spoken_emoji_1F629" msgid="1411538490319190118">"មុខនឿយហត់"</string> <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"មុខងងុយគេង"</string> - <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"មុខអស់កម្លាំង"</string> - <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"មុខក្រញេវក្រញូវ"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"មុខអស់កម្លាំង"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"មុខក្រញេវក្រញូវ"</string> <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"មុខយំលឺៗ"</string> <string name="spoken_emoji_1F62E" msgid="726083405284353894">"មុខបើកមាត់"</string> <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"មុខស្ងៀមស្ងាត់"</string> @@ -784,7 +784,7 @@ <string name="spoken_emoji_1F683" msgid="8772750354339223092">"ទូររថភ្លើង"</string> <string name="spoken_emoji_1F684" msgid="346396777356203608">"រថភ្លើងល្បឿនលឿន"</string> <string name="spoken_emoji_1F685" msgid="1237059817190832730">"រថភ្លើងល្បឿនលឿនមានច្រមុះ"</string> - <string name="spoken_emoji_1F686" msgid="3525197227223620343">"រថភ្លើង"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"រថភ្លើង"</string> <string name="spoken_emoji_1F687" msgid="5110143437960392837">"មេត្រូ"</string> <string name="spoken_emoji_1F688" msgid="4702085029871797965">"រថភ្លើងប្រើពន្លឺ"</string> <string name="spoken_emoji_1F689" msgid="2375851019798817094">"ស្ថានីយ"</string> @@ -803,7 +803,7 @@ <string name="spoken_emoji_1F696" msgid="6391604457418285404">"តាក់ស៊ីខាងមុខ"</string> <string name="spoken_emoji_1F697" msgid="7978399334396733790">"រថយន្ត"</string> <string name="spoken_emoji_1F698" msgid="7006050861129732018">"រថយន្តខាងមុខ"</string> - <string name="spoken_emoji_1F699" msgid="630317052666590607">"រថយន្តសម្រាប់កម្សាន្ត"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"រថយន្តសម្រាប់កម្សាន្ត"</string> <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"រថយន្តចែកចាយ"</string> <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"ឡានកាមីយ៉ុង"</string> <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"ត្រាក់ទ័រ"</string> @@ -819,7 +819,7 @@ <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"ភ្លើងចរាចរណ៍បញ្ឈរ"</string> <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"សញ្ញាសំណង់"</string> <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"រថយន្តប៉ូលិសបើកសារ៉ែនវិល"</string> - <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"បង្គោលទង់ជាតិរាងត្រីកោណ"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"បង្គោលទង់ជាតិរាងត្រីកោណ"</string> <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"ទ្វារ"</string> <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"សញ្ញាហាមចូល"</string> <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"សញ្ញាជក់បារី"</string> diff --git a/java/res/values-km-rKH/strings-talkback-descriptions.xml b/java/res/values-km-rKH/strings-talkback-descriptions.xml index 29d3b959f..f9078323d 100644 --- a/java/res/values-km-rKH/strings-talkback-descriptions.xml +++ b/java/res/values-km-rKH/strings-talkback-descriptions.xml @@ -78,7 +78,7 @@ <string name="spoken_emoji_unknown" msgid="5981009928135394306">"មិនស្គាល់សញ្ញាអារម្មណ៍"</string> <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"មុខអផ្សុក"</string> <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"មុខខ្មាសអៀន"</string> - <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"ពាក់វ៉ែនតា"</string> + <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"ពាក់វ៉ែនតា"</string> <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"មុខភ្ញាក់ផ្អើល"</string> <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"មុខថើប"</string> <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"មុខចងចិញ្ចើម"</string> diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml index eb2819427..8aea853a2 100644 --- a/java/res/values-km-rKH/strings.xml +++ b/java/res/values-km-rKH/strings.xml @@ -39,7 +39,7 @@ <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"គ្រាប់ចុចប្ដូរភាសាតាមវិធីសាស្ត្របញ្ចូលផ្សេងទៀត"</string> <string name="show_language_switch_key" msgid="5915478828318774384">"គ្រាប់ចុចប្ដូរភាសា"</string> <string name="show_language_switch_key_summary" msgid="7343403647474265713">"បង្ហាញនៅពេលដែលបើកភាសាបញ្ចូលច្រើន"</string> - <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"សោលេចឡើងបោះបង់ការពន្យារពេល"</string> + <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"សោលេចឡើងបោះបង់ការពន្យារពេល"</string> <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"គ្មានការពន្យារពេល"</string> <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"លំនាំដើម"</string> <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> មិល្លីវិនាទី"</string> @@ -50,7 +50,7 @@ <string name="enable_metrics_logging" msgid="5506372337118822837">"ធ្វើឲ្យ <xliff:g id="APPLICATION_NAME">%s</xliff:g> ប្រសើរឡើង"</string> <string name="use_double_space_period" msgid="8781529969425082860">"រយៈពេលចុចដកឃ្លាពីរដង"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"ប៉ះដកឃ្លាពីរដងបញ្ចូលរយៈពេលដែលអនុវត្តតាមដកឃ្លា"</string> - <string name="auto_cap" msgid="1719746674854628252">"ការសរសេរជាអក្សរធំស្វ័យប្រវត្តិ"</string> + <string name="auto_cap" msgid="1719746674854628252">"ការសរសេរជាអក្សរធំស្វ័យប្រវត្តិ"</string> <string name="auto_cap_summary" msgid="7934452761022946874">"សរសេរពាក្យដំបូងជាអក្សរធំនៃប្រយោគនីមួយៗ"</string> <string name="edit_personal_dictionary" msgid="3996910038952940420">"វចនានុក្រមផ្ទាល់ខ្លួន"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"ផ្នែកបន្ថែមវចនានុក្រម"</string> @@ -58,7 +58,7 @@ <string name="prefs_show_suggestions" msgid="8026799663445531637">"បង្ហាញការស្នើកែ"</string> <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"បង្ហាញពាក្យបានផ្ដល់ស្នើខណៈពេលវាយបញ្ចូល"</string> <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"ទប់ស្កាត់ពាក្យបំពាន"</string> - <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"កុំស្នើឲ្យពាក្យបំពានមានសក្ដានុពល"</string> + <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"កុំស្នើឲ្យពាក្យបំពានមានសក្ដានុពល"</string> <string name="auto_correction" msgid="7630720885194996950">"ការកែស្វ័យប្រវត្តិ"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"ចន្លោះមិនឃើញ និងសញ្ញាវណ្ណយុត្តកែពាក្យដែលបានវាយខុសស្វ័យប្រវត្តិ"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"បិទ"</string> @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"សើប (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (អក្សរពេញ)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (បង្រួម)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"គ្មានភាសា (អក្សរក្រម)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"តាមលំដាប់អក្សរក្រម (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"តាមលំដាប់អក្សរក្រម (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"មានរចនាប័ទ្មបញ្ចូលដូចគ្នាដូចហើយ៖ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ថិរវេលាញ័រពេលចុចគ្រាប់ចុច"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"កម្រិតសំឡេងពេលចុចគ្រាប់ចុច"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ពន្យារពេលចុចគ្រាប់ចុចឲ្យយូរ"</string> <string name="button_default" msgid="3988017840431881491">"លំនាំដើម"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"សូមស្វាគមន៍មកកាន់ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ជាមួយការវាយបញ្ចូលដោយប្រើកាយវិការ"</string> @@ -147,7 +149,7 @@ <string name="dictionary_provider_name" msgid="3027315045397363079">"កម្មវិធីផ្ដល់វចនានុក្រម"</string> <string name="dictionary_service_name" msgid="6237472350693511448">"សេវាកម្មវចនានុក្រម"</string> <string name="download_description" msgid="6014835283119198591">"ព័ត៌មានបច្ចុប្បន្នភាពវចនានុក្រម"</string> - <string name="dictionary_settings_title" msgid="8091417676045693313">"ផ្នែកបន្ថែមវចនានុក្រម"</string> + <string name="dictionary_settings_title" msgid="8091417676045693313">"ផ្នែកបន្ថែមវចនានុក្រម"</string> <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"វចនានុក្រមអាចប្រើបាន"</string> <string name="dictionary_settings_summary" msgid="5305694987799824349">"ការកំណត់សម្រាប់វចនានុក្រម"</string> <string name="user_dictionaries" msgid="3582332055892252845">"វចនានុក្រមអ្នកប្រើ"</string> @@ -163,10 +165,10 @@ <string name="message_updating" msgid="4457761393932375219">"ពិនិត្យមើលបច្ចុប្បន្នភាព"</string> <string name="message_loading" msgid="5638680861387748936">"កំពុងផ្ទុក..."</string> <string name="main_dict_description" msgid="3072821352793492143">"វចនានុក្រមចម្បង"</string> - <string name="cancel" msgid="6830980399865683324">"បោះបង់"</string> + <string name="cancel" msgid="6830980399865683324">"បោះបង់"</string> <string name="go_to_settings" msgid="3876892339342569259">"ការកំណត់"</string> <string name="install_dict" msgid="180852772562189365">"ដំឡើង"</string> - <string name="cancel_download_dict" msgid="7843340278507019303">"បោះបង់"</string> + <string name="cancel_download_dict" msgid="7843340278507019303">"បោះបង់"</string> <string name="delete_dict" msgid="756853268088330054">"លុប"</string> <string name="should_download_over_metered_prompt" msgid="1583881200688185508">"ភាសាដែលបានជ្រើសនៅលើឧបករណ៍ចល័តមានវចនានុក្រមអាចប្រើបាន។<br/> យើងផ្ដល់អនុសាសន៍ឲ្យ <b>ទាញយក</b> វចនានុក្រមភាសា <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> ដើម្បីបង្កើនបទពិសោធន៍វាយបញ្ចូលរបស់អ្នក។<br/> <br/> ការទាញយកអាចចំណាយពេលប្រហែលពីរនាទីនៅតាម 3G។ ការគិតថ្លៃអាចអនុវត្តប្រសិនបើអ្នកមិនប្រើ <b>ផែនការទិន្នន័យគ្មានដែនកំណត់</b>.<br/> បើអ្នកមិនប្រាកដថាផែនការណាមួយដែលអ្នកមាន យើងផ្ដល់អនុសាសន៍ឲ្យភ្ជាប់វ៉ាយហ្វាយ ដើម្បីចាប់ផ្ដើមទាញយកដោយស្វ័យប្រវត្តិ។<br/> <br/> ជំនួយ៖ អ្នកអាចទាញយក និងលុបវចនានុក្រមដោយចូលទៅ <b>ភាសា & ការបញ្ចូល</b> នៅក្នុងម៉ឺនុយ <b>ការកំណត់</b> សម្រាប់ឧបករណ៍ចល័ត។"</string> <string name="download_over_metered" msgid="1643065851159409546">"ទាញយកឥឡូវនេះ (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> មេកាបៃ)"</string> @@ -184,7 +186,7 @@ <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"ពាក្យ៖"</string> <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ផ្លូវកាត់៖"</string> <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ភាសា៖"</string> - <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"វាយបញ្ចូលពាក្យ"</string> + <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"វាយបញ្ចូលពាក្យ"</string> <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"ផ្លូវកាត់ជាជម្រើស"</string> <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"កែពាក្យ"</string> <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"កែ"</string> diff --git a/java/res/values-kn-rIN/strings.xml b/java/res/values-kn-rIN/strings.xml index eb454ccf3..04d9b1c49 100644 --- a/java/res/values-kn-rIN/strings.xml +++ b/java/res/values-kn-rIN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ಹಿಂಗ್ಲಿಷ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ಸರ್ಬಿಯನ್ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ಸಾಂಪ್ರದಾಯಿಕ)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ಕಾಂಪ್ಯಾಕ್ಟ್)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ಯಾವುದೇ ಭಾಷೆಯಿಲ್ಲ (ವರ್ಣಮಾಲೆ)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ವರ್ಣಮಾಲೆ (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ವರ್ಣಮಾಲೆ (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ಅದೇ ರೀತಿಯ ಇನ್ಪುಟ್ ಶೈಲಿಯು ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ಕೀಒತ್ತುವ ವೈಬ್ರೇಷನ್ ಅವಧಿ"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ಕೀಒತ್ತುವ ಶಬ್ದದ ವಾಲ್ಯೂಮ್"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ಕೀಯ ದೀರ್ಘ ಒತ್ತುವ ವಿಳಂಬ"</string> <string name="button_default" msgid="3988017840431881491">"ಡೀಫಾಲ್ಟ್"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> ಗೆ ಸುಸ್ವಾಗತ"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ಗೆಶ್ಚರ್ ಟೈಪಿಂಗ್ನೊಂದಿಗೆ"</string> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index a778a49b8..68195f0d1 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"인도 영어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"세르비아어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(번체)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(컴팩트)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"언어 없음(알파벳)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"알파벳(QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"알파벳(QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"같은 입력 스타일이 다음과 같이 이미 존재합니다. <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"키를 누를 때 진동 시간"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"키를 누를 때 소리 볼륨"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"키 길게 누르기 지연"</string> <string name="button_default" msgid="3988017840431881491">"기본값"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>에 오신 것을 환영합니다."</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"제스처 타이핑 사용"</string> diff --git a/java/res/values-ky-rKG/strings.xml b/java/res/values-ky-rKG/strings.xml index d99eeb5f2..6b46785e3 100644 --- a/java/res/values-ky-rKG/strings.xml +++ b/java/res/values-ky-rKG/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сербче (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Салттык)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Чакан)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Тил жок (Алфавит)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Алфавит (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Алфавит (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Мындай жазуу стили мурунтан бар: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Басылган баскычтын дирлдөө узактгы"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Басылган баскычтын үнүнүн катуулугу"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Баскычты көпкө басууну кечиктирүү"</string> <string name="button_default" msgid="3988017840431881491">"Демейки"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> кош келиңиз"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Жаңсап терүү менен"</string> diff --git a/java/res/values-lo-rLA/strings-emoji-descriptions.xml b/java/res/values-lo-rLA/strings-emoji-descriptions.xml index 83a702e95..84b9d0502 100644 --- a/java/res/values-lo-rLA/strings-emoji-descriptions.xml +++ b/java/res/values-lo-rLA/strings-emoji-descriptions.xml @@ -210,7 +210,7 @@ <string name="spoken_emoji_1F330" msgid="3115760035618051575">"ລູກເກົາລັດ"</string> <string name="spoken_emoji_1F331" msgid="5658888205290008691">"ກ້າໄມ້"</string> <string name="spoken_emoji_1F332" msgid="2935650450421165938">"ຕົ້ນໄມ້ບໍ່ຜັດໃບ"</string> - <string name="spoken_emoji_1F333" msgid="5898847427062482675">"ຕົ້ນໄມ້ຜັດໃບ"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"ຕົ້ນໄມ້ຜັດໃບ"</string> <string name="spoken_emoji_1F334" msgid="6183375224678417894">"ຕົ້ນປາມ"</string> <string name="spoken_emoji_1F335" msgid="5352418412103584941">"ກະບອງເພັດ"</string> <string name="spoken_emoji_1F337" msgid="3839107352363566289">"ທິວລິບ"</string> @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"ຄຸກກີ້"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ຊັອກໂກແລັດບາ"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ແຄນດີ້"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ໂລລິປັອບ"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"ຄັສຕາດ"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ໝໍ້ນ້ຳເຜິ້ງ"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ຊັອດເຄັກ"</string> @@ -450,7 +450,7 @@ <string name="spoken_emoji_1F458" msgid="5704243858031107692">"ກິໂມໂນ"</string> <string name="spoken_emoji_1F459" msgid="3553148747050035251">"ບິກີນີ"</string> <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"ເສື້ອຜ້າຜູ່ຍິງ"</string> - <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"ກະເປົາ"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"ກະເປົາ"</string> <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"ກະເປົາ"</string> <string name="spoken_emoji_1F45D" msgid="812176504300064819">"ກະເປົາ"</string> <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"ເກີບຜູ່ຊາຍ"</string> diff --git a/java/res/values-lo-rLA/strings-letter-descriptions.xml b/java/res/values-lo-rLA/strings-letter-descriptions.xml index 47f7cbc81..ecc0b7a71 100644 --- a/java/res/values-lo-rLA/strings-letter-descriptions.xml +++ b/java/res/values-lo-rLA/strings-letter-descriptions.xml @@ -186,7 +186,7 @@ <string name="spoken_symbol_2019" msgid="8892530161598134083">"Right single quotation mark"</string> <string name="spoken_symbol_201A" msgid="2072987157683446644">"Single low-9 quotation mark"</string> <string name="spoken_symbol_201C" msgid="4588048378803665427">"ເຄື່ອງໝາຍວົງຢືມຊ້າຍ"</string> - <string name="spoken_symbol_201D" msgid="1642776849495925895">"ເຄື່ອງໝາຍວົງຢືມຂວາ"</string> + <string name="spoken_symbol_201D" msgid="1642776849495925895">"ເຄື່ອງໝາຍວົງຢືມຂວາ"</string> <string name="spoken_symbol_2020" msgid="9084628638189344431">"Dagger"</string> <string name="spoken_symbol_2021" msgid="5081396468559426475">"Double dagger"</string> <string name="spoken_symbol_2030" msgid="9068837172419431755">"ເຄື່ອງໝາຍຕໍ່ໄມລ໌"</string> diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml index bdc99fc9f..efc09bebc 100644 --- a/java/res/values-lo-rLA/strings.xml +++ b/java/res/values-lo-rLA/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ຮິງລິສ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ເຊີບຽນ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ດັ້ງເດີມ)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ກະທັດຮັດ)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ບໍ່ມີພາສາ (ໂຕອັກສອນ)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ໂຕອັກສອນ (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ໂຕອັກສອນ (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ຮູບແບບການປ້ອນຂໍ້ມູນທີ່ຄືກັນມີຢູ່ແລ້ວ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ໄລຍະເວລາຂອງການສັ່ນໃນການກົດປຸ່ມ"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ລະດັບສຽງຂອງການກົດປຸ່ມ"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ໄລຍະເວລາຂອງການກົດປຸ່ມ"</string> <string name="button_default" msgid="3988017840431881491">"ຄ່າເລີ່ມຕົ້ນ"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"ຍິນດີຕ້ອນຮັບສູ່ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ດ້ວຍການພິມແບບ Gesture"</string> diff --git a/java/res/values-lt/strings-emoji-descriptions.xml b/java/res/values-lt/strings-emoji-descriptions.xml index fa81dbbd8..afe9ac0e6 100644 --- a/java/res/values-lt/strings-emoji-descriptions.xml +++ b/java/res/values-lt/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sausainis"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Šokolado plytelė"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Saldainis"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Ledinukas ant pagaliuko"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Saldus kremas"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Medaus puodynė"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Trapus pyragas"</string> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index 147d706bb..cc8d0eaf6 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi ir anglų derinys (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbų k. (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicinė)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktiška)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Kalbos nėra (abėcėlė)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abėcėlė (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abėcėlė (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Toks pat įvesties stilius jau yra: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrav. paspaudus mygt. trukmė"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Garso paspaudus mygt. garsumas"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavišo ilgo paspaudimo delsa"</string> <string name="button_default" msgid="3988017840431881491">"Numatytieji"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Sveiki! Tai „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"naudojant įvestį gestais"</string> diff --git a/java/res/values-lv/strings-emoji-descriptions.xml b/java/res/values-lv/strings-emoji-descriptions.xml index a51991b81..525a1a0e8 100644 --- a/java/res/values-lv/strings-emoji-descriptions.xml +++ b/java/res/values-lv/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Cepums"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Šokolādes tāfelīte"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Konfekte"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Cukurgailītis"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Olu krēms"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Medus pods"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Smilšu torte ar augļu pildījumu"</string> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 812cb9a0d..833839b8a 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi–angļu valoda (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbu (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionālā)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktā)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Nav valodas (alfabēts)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabēts (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabēts (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Šāds ievades stils jau pastāv: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Taust. nosp. vibrācijas ilgums"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Taustiņu nosp. skaņas skaļums"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Taustiņa ilgās nosp. noildze"</string> <string name="button_default" msgid="3988017840431881491">"Noklusējums"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Laipni lūdzam pakalpojumā <xliff:g id="APPLICATION_NAME">%s</xliff:g>,"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kurā varat izmantot ievadi ar žestiem"</string> diff --git a/java/res/values-mk-rMK/strings.xml b/java/res/values-mk-rMK/strings.xml index ac2dd12e6..f3503f7bf 100644 --- a/java/res/values-mk-rMK/strings.xml +++ b/java/res/values-mk-rMK/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Српски (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционален)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Компактна)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Нема јазик (азбука)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Азбука (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Азбука (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Истиот стил на влез веќе постои: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Траење на вибрација од копче"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Копче за јачина на звук"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Доцнење на долго притискање копче"</string> <string name="button_default" msgid="3988017840431881491">"Стандардно"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Добре дојдовте во <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"со Пишување со движење"</string> diff --git a/java/res/values-ml-rIN/strings-emoji-descriptions.xml b/java/res/values-ml-rIN/strings-emoji-descriptions.xml index a846f31a5..ab6509756 100644 --- a/java/res/values-ml-rIN/strings-emoji-descriptions.xml +++ b/java/res/values-ml-rIN/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"കുക്കി"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ചോക്കലേറ്റ് ബാർ"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"മിഠായി"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ലോലിപോപ്പ്"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"കസ്റ്റാർഡ്"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"തേൻ കുടം"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ഷോർട്ട്കേക്ക്"</string> diff --git a/java/res/values-ml-rIN/strings-talkback-descriptions.xml b/java/res/values-ml-rIN/strings-talkback-descriptions.xml index b3f17672c..3bed53786 100644 --- a/java/res/values-ml-rIN/strings-talkback-descriptions.xml +++ b/java/res/values-ml-rIN/strings-talkback-descriptions.xml @@ -58,7 +58,7 @@ <string name="keyboard_mode_date" msgid="6597407244976713364">"തീയതി"</string> <string name="keyboard_mode_date_time" msgid="3642804408726668808">"തീയതിയും സമയവും"</string> <string name="keyboard_mode_email" msgid="1239682082047693644">"ഇമെയിൽ"</string> - <string name="keyboard_mode_im" msgid="3812086215529493501">"സന്ദേശമയയ്ക്കൽ"</string> + <string name="keyboard_mode_im" msgid="3812086215529493501">"സന്ദേശം"</string> <string name="keyboard_mode_number" msgid="5395042245837996809">"നമ്പർ"</string> <string name="keyboard_mode_phone" msgid="2486230278064523665">"ഫോൺ"</string> <string name="keyboard_mode_text" msgid="9138789594969187494">"ടെക്സ്റ്റ്"</string> diff --git a/java/res/values-ml-rIN/strings.xml b/java/res/values-ml-rIN/strings.xml index 61099cb02..a34de64ac 100644 --- a/java/res/values-ml-rIN/strings.xml +++ b/java/res/values-ml-rIN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ഹിംഗ്ലീഷ് (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"സെർബിയൻ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (പരമ്പരാഗതം)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (കോംപാക്ട്)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ഭാഷയില്ല (അക്ഷരമാല)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"അക്ഷരമാല (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"അക്ഷരമാല (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"സമാന ടൈപ്പുചെയ്യൽ ശൈലി ഇതിനകം നിലവിലുണ്ട്: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"കീ അമർത്തുമ്പോഴുള്ള വൈബ്രേഷൻ ദൈർഘ്യം"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"കീ അമർത്തുമ്പോഴുള്ള ശബ്ദ വോളിയം"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"കീ ദീർഘനേരം അമർത്തിപ്പിടിക്കൽ കാലതാമസം"</string> <string name="button_default" msgid="3988017840431881491">"സ്ഥിരമായത്"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> എന്നതിലേക്ക് സ്വാഗതം"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ജെസ്റ്റർ ടൈപ്പുചെയ്യലിനൊപ്പം"</string> diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml index e61584b7e..938c90f29 100644 --- a/java/res/values-mn-rMN/strings.xml +++ b/java/res/values-mn-rMN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Серьби хэл (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (уламжлалт)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Компакт)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Хэл байхгүй (Цагаан толгой)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Цагаан толгой (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Цагаан толгой (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ижилхэн оруулах загвар байна: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Товч дарах чичиргээний хугацаа"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Товчны дууны хэмжээ"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Товч удаан дарах хугацааны тохиргоо"</string> <string name="button_default" msgid="3988017840431881491">"Үндсэн"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Та <xliff:g id="APPLICATION_NAME">%s</xliff:g>-д тавтай морилно уу"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Зангаагаар бичихээр"</string> diff --git a/java/res/values-mr-rIN/strings.xml b/java/res/values-mr-rIN/strings.xml index 339f701e3..2c6746e35 100644 --- a/java/res/values-mr-rIN/strings.xml +++ b/java/res/values-mr-rIN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियन (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपारिक)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (संक्षिप्त)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"भाषा नाही (वर्णमाला)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"समान इनपुट शैली आधीपासूनच अस्तित्वात आहे: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कीप्रेस कंपन कालावधी"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कीप्रेस ध्वनी तीव्रता"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"की जास्त दाबण्यात विलंब"</string> <string name="button_default" msgid="3988017840431881491">"डीफॉल्ट"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> वर स्वागत आहे"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"जेश्चर टायपिंग करून"</string> @@ -168,9 +170,9 @@ <string name="install_dict" msgid="180852772562189365">"स्थापित करा"</string> <string name="cancel_download_dict" msgid="7843340278507019303">"रद्द करा"</string> <string name="delete_dict" msgid="756853268088330054">"हटवा"</string> - <string name="should_download_over_metered_prompt" msgid="1583881200688185508">"आपल्या मोबाईल डिव्हाइसवर निवडलेल्या भाषेमध्ये शब्दकोश उपलब्ध आहे.<br/> आम्ही आपला टायपिंग अनुभव सुधारण्यासाठी <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> शब्दकोश <b>डाउनलोड करण्याची</b> शिफारस करतो.<br/> <br/> डाउनलोड कऱण्यास 3G वर एक किंवा दोन मिनिट लागू शकतात. आपल्याकडे <b>अमर्यादित डेटा योजना</b> नसल्यास शुल्क लागू शकते. आपल्याकडे असलेल्या डेटा योजनेबद्दल आपण सुनिश्चित नसल्यास, डाउनलोड स्वयंचलितपणे प्रारंभ करण्यासाठी Wi-Fi कनेक्शन शोधण्याची शिफारस आम्ही करतो.<br/> <br/> टीप: आपण आपल्या मोबाईल डिव्हाइसच्या <b>सेटिंग्ज</b>मेनूमधील <b>भाषा आणि इनपुट</b> वर जाऊन शब्दकोश डाउनलोड करू आणि काढू शकता."</string> + <string name="should_download_over_metered_prompt" msgid="1583881200688185508">"आपल्या मोबाईल डिव्हाइसवर निवडलेल्या भाषेमध्ये शब्दकोश उपलब्ध आहे.<br/> आम्ही आपला टायपिंग अनुभव सुधारण्यासाठी <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> शब्दकोश <b>डाउनलोड करण्याची</b> शिफारस करतो.<br/> <br/> डाउनलोड कऱण्यास 3G वर एक किंवा दोन मिनिट लागू शकतात. आपल्याकडे <b>अमर्यादित डेटा योजना</b> नसल्यास शुल्क लागू शकते. आपल्याकडे असलेल्या डेटा योजनेबद्दल आपण सुनिश्चित नसल्यास, डाउनलोड स्वयंचलितपणे प्रारंभ करण्यासाठी वाय-फाय कनेक्शन शोधण्याची शिफारस आम्ही करतो.<br/> <br/> टीप: आपण आपल्या मोबाईल डिव्हाइसच्या <b>सेटिंग्ज</b>मेनूमधील <b>भाषा आणि इनपुट</b> वर जाऊन शब्दकोश डाउनलोड करू आणि काढू शकता."</string> <string name="download_over_metered" msgid="1643065851159409546">"आता डाउनलोड करा (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string> - <string name="do_not_download_over_metered" msgid="2176209579313941583">"Wi-Fi वर डाउनलोड करा"</string> + <string name="do_not_download_over_metered" msgid="2176209579313941583">"वाय-फाय वर डाउनलोड करा"</string> <string name="dict_available_notification_title" msgid="4583842811218581658">"<xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> साठी शब्दकोश उपलब्ध आहे"</string> <string name="dict_available_notification_description" msgid="1075194169443163487">"पुनरावलोकन करण्यासाठी आणि डाउनलोड करण्यासाठी दाबा"</string> <string name="toast_downloading_suggestions" msgid="6128155879830851739">"डाउनलोड करत आहे: <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> साठी सूचना लवकरच तयार होतील."</string> diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml index 07d86c52b..3136c3bbd 100644 --- a/java/res/values-ms-rMY/strings.xml +++ b/java/res/values-ms-rMY/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Bahasa Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Sarat)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Tiada bahasa (Abjad)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tempoh getaran tekan kekunci"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Kelantangan bunyi tekan kekunci"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Kelewatan tekan lama kekunci"</string> <string name="button_default" msgid="3988017840431881491">"Lalai"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang ke <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Taipan Gerak Isyarat"</string> diff --git a/java/res/values-my-rMM/strings-action-keys.xml b/java/res/values-my-rMM/strings-action-keys.xml index f7a2ca9e1..d15c9e55e 100644 --- a/java/res/values-my-rMM/strings-action-keys.xml +++ b/java/res/values-my-rMM/strings-action-keys.xml @@ -27,5 +27,5 @@ <string name="label_send_key" msgid="482252074224462163">"ပို့ရန်"</string> <string name="label_search_key" msgid="7965186050435796642">"ရှာဖွေရန်"</string> <string name="label_pause_key" msgid="2225922926459730642">"ဆိုင်းငံ့ရန်"</string> - <string name="label_wait_key" msgid="5891247853595466039">"စောင့်ဆိုင်းရန်"</string> + <string name="label_wait_key" msgid="5891247853595466039">"စောင့်ဆိုင်းရန်"</string> </resources> diff --git a/java/res/values-my-rMM/strings-letter-descriptions.xml b/java/res/values-my-rMM/strings-letter-descriptions.xml index d904f53f6..2d5338b86 100644 --- a/java/res/values-my-rMM/strings-letter-descriptions.xml +++ b/java/res/values-my-rMM/strings-letter-descriptions.xml @@ -29,7 +29,7 @@ <string name="spoken_accented_letter_00AA" msgid="4374325261868451570">"ဣထိလိင် အစဉ်ပြ အညွှန်း"</string> <string name="spoken_accented_letter_00B5" msgid="9031387673828823891">"မိုက်ခရို သင်္ကေတ"</string> <string name="spoken_accented_letter_00BA" msgid="5045198452071207437">"ပုလိင် အစဉ်ပြ အညွှန်း"</string> - <string name="spoken_accented_letter_00DF" msgid="2260098367028134281">"ပြတ်သားသည့် S"</string> + <string name="spoken_accented_letter_00DF" msgid="2260098367028134281">"ပြတ်သားသည့် S"</string> <string name="spoken_accented_letter_00E0" msgid="2234515772182387086">"A၊ တည်ငြိမ်သော"</string> <string name="spoken_accented_letter_00E1" msgid="7780174500802535063">"A၊ စူးရှသော"</string> <string name="spoken_accented_letter_00E2" msgid="7054108480488102631">"A၊ သရသံသင်္ကေတ"</string> @@ -133,45 +133,45 @@ <string name="spoken_accented_letter_0259" msgid="2464085263158415898">"Schwa"</string> <string name="spoken_accented_letter_1EA1" msgid="688124877202887630">"A၊ အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EA3" msgid="327960130366386256">"A အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1EA5" msgid="637406363453769610">"A၊ သရသံသင်္ကေတ နှင့် စူးရှသော"</string> - <string name="spoken_accented_letter_1EA7" msgid="1419591804181615409">"A၊ သရသံသင်္ကေတ နှင့် တည်ငြိမ်သော"</string> - <string name="spoken_accented_letter_1EA9" msgid="6068887382734896756">"A၊ သရသံသင်္ကေတ နှင့် အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1EAB" msgid="7236523151662538333">"A၊ သရသံသင်္ကေတ နှင့် tilde"</string> - <string name="spoken_accented_letter_1EAD" msgid="2363364864106332076">"A,၊ သရသံသင်္ကေတ နှင့် အောက်မှာ အစက်"</string> - <string name="spoken_accented_letter_1EAF" msgid="1576329511464272935">"A၊ တည်ငြိမ်သော နှင့် စူးရှသော"</string> - <string name="spoken_accented_letter_1EB1" msgid="4634735072816076592">"A၊ breve နှင့် တည်ငြိမ်သော"</string> - <string name="spoken_accented_letter_1EB3" msgid="2325245849038771534">"A၊ breve နှင့် အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1EB5" msgid="3720427596242746295">"A၊ breve နှင့် tilde"</string> - <string name="spoken_accented_letter_1EB7" msgid="700415535653646695">"A၊ breve နှင့် အောက်မှာ အစက်"</string> + <string name="spoken_accented_letter_1EA5" msgid="637406363453769610">"A၊ သရသံသင်္ကေတ နှင့် စူးရှသော"</string> + <string name="spoken_accented_letter_1EA7" msgid="1419591804181615409">"A၊ သရသံသင်္ကေတ နှင့် တည်ငြိမ်သော"</string> + <string name="spoken_accented_letter_1EA9" msgid="6068887382734896756">"A၊ သရသံသင်္ကေတ နှင့် အပေါ်မှာ ချိတ်"</string> + <string name="spoken_accented_letter_1EAB" msgid="7236523151662538333">"A၊ သရသံသင်္ကေတ နှင့် tilde"</string> + <string name="spoken_accented_letter_1EAD" msgid="2363364864106332076">"A,၊ သရသံသင်္ကေတ နှင့် အောက်မှာ အစက်"</string> + <string name="spoken_accented_letter_1EAF" msgid="1576329511464272935">"A၊ တည်ငြိမ်သော နှင့် စူးရှသော"</string> + <string name="spoken_accented_letter_1EB1" msgid="4634735072816076592">"A၊ breve နှင့် တည်ငြိမ်သော"</string> + <string name="spoken_accented_letter_1EB3" msgid="2325245849038771534">"A၊ breve နှင့် အပေါ်မှာ ချိတ်"</string> + <string name="spoken_accented_letter_1EB5" msgid="3720427596242746295">"A၊ breve နှင့် tilde"</string> + <string name="spoken_accented_letter_1EB7" msgid="700415535653646695">"A၊ breve နှင့် အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EB9" msgid="3901338692305890487">"E၊ အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EBB" msgid="4028688699415417302">"E၊ အပေါ်မှာ ချိတ်"</string> <string name="spoken_accented_letter_1EBD" msgid="181253633045931897">"E၊ tilde"</string> - <string name="spoken_accented_letter_1EBF" msgid="3309618845007944963">"E၊ သရသံသင်္ကေတ နှင့် စူးရှသော"</string> - <string name="spoken_accented_letter_1EC1" msgid="8139046749226332542">"E၊ သရသံသင်္ကေတ နှင့် တည်ငြိမ်သော"</string> - <string name="spoken_accented_letter_1EC3" msgid="3239674223053133383">"E၊ သရသံသင်္ကေတ နှင့် ချိတ် အပေါ်မှာ"</string> - <string name="spoken_accented_letter_1EC5" msgid="2216559244705714587">"E၊ သရသံသင်္ကေတ နှင့် tilde"</string> - <string name="spoken_accented_letter_1EC7" msgid="9012731468253986792">"E၊ သရသံသင်္ကေတ နှင့် အောက်မှာ အစက်"</string> + <string name="spoken_accented_letter_1EBF" msgid="3309618845007944963">"E၊ သရသံသင်္ကေတ နှင့် စူးရှသော"</string> + <string name="spoken_accented_letter_1EC1" msgid="8139046749226332542">"E၊ သရသံသင်္ကေတ နှင့် တည်ငြိမ်သော"</string> + <string name="spoken_accented_letter_1EC3" msgid="3239674223053133383">"E၊ သရသံသင်္ကေတ နှင့် ချိတ် အပေါ်မှာ"</string> + <string name="spoken_accented_letter_1EC5" msgid="2216559244705714587">"E၊ သရသံသင်္ကေတ နှင့် tilde"</string> + <string name="spoken_accented_letter_1EC7" msgid="9012731468253986792">"E၊ သရသံသင်္ကေတ နှင့် အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EC9" msgid="2901917620195717034">"I၊ အပေါ်မှာ ချိတ်"</string> <string name="spoken_accented_letter_1ECB" msgid="5470387489820034621">"I၊ အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1ECD" msgid="1340122876914839806">"O၊ အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1ECF" msgid="2326921263882559755">"O၊ အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1ED1" msgid="2885683296042774958">"O၊ သရသံသင်္ကေတ နှင့် စူးရှသော"</string> - <string name="spoken_accented_letter_1ED3" msgid="6857664926477376178">"O၊ သရသံသင်္ကေတ နှင့် တည်ငြိမ်သော"</string> - <string name="spoken_accented_letter_1ED5" msgid="2015209467290624062">"O၊ သရသံသင်္ကေတ နှင့် အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1ED7" msgid="7924481354960306389">"O၊ သရသံသင်္ကေတ နှင့် tilde"</string> - <string name="spoken_accented_letter_1ED9" msgid="7023315590332365554">"O၊ သရသံသင်္ကေတ နှင့် အောက်မှာ အစက်"</string> - <string name="spoken_accented_letter_1EDB" msgid="2379438944917634496">"O၊ horn နှင့် စူးရှသော"</string> - <string name="spoken_accented_letter_1EDD" msgid="8107077534204404085">"O၊ horn နှင့် တည်ငြိမ်သော"</string> - <string name="spoken_accented_letter_1EDF" msgid="1846880105528347966">"O၊ horn နှင့် အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1EE1" msgid="1520037313389776718">"O၊ horn နှင့် tilde"</string> - <string name="spoken_accented_letter_1EE3" msgid="907964027171008963">"O၊ horn နှင့် အောက်မှာ အစက်"</string> + <string name="spoken_accented_letter_1ED1" msgid="2885683296042774958">"O၊ သရသံသင်္ကေတ နှင့် စူးရှသော"</string> + <string name="spoken_accented_letter_1ED3" msgid="6857664926477376178">"O၊ သရသံသင်္ကေတ နှင့် တည်ငြိမ်သော"</string> + <string name="spoken_accented_letter_1ED5" msgid="2015209467290624062">"O၊ သရသံသင်္ကေတ နှင့် အပေါ်မှာ ချိတ်"</string> + <string name="spoken_accented_letter_1ED7" msgid="7924481354960306389">"O၊ သရသံသင်္ကေတ နှင့် tilde"</string> + <string name="spoken_accented_letter_1ED9" msgid="7023315590332365554">"O၊ သရသံသင်္ကေတ နှင့် အောက်မှာ အစက်"</string> + <string name="spoken_accented_letter_1EDB" msgid="2379438944917634496">"O၊ horn နှင့် စူးရှသော"</string> + <string name="spoken_accented_letter_1EDD" msgid="8107077534204404085">"O၊ horn နှင့် တည်ငြိမ်သော"</string> + <string name="spoken_accented_letter_1EDF" msgid="1846880105528347966">"O၊ horn နှင့် အပေါ်မှာ ချိတ်"</string> + <string name="spoken_accented_letter_1EE1" msgid="1520037313389776718">"O၊ horn နှင့် tilde"</string> + <string name="spoken_accented_letter_1EE3" msgid="907964027171008963">"O၊ horn နှင့် အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EE5" msgid="1522024630360038700">"U၊ အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EE7" msgid="7815412228302952637">"U၊ အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1EE9" msgid="4219119671251485651">"U၊ horn နှင့် စူးရှသော"</string> - <string name="spoken_accented_letter_1EEB" msgid="4086009841269002231">"U၊ horn နှင့် တည်ငြိမ်သော"</string> - <string name="spoken_accented_letter_1EED" msgid="3528151733528719847">"U၊ horn နှင့် အပေါ်မှာ ချိတ်"</string> - <string name="spoken_accented_letter_1EEF" msgid="3508548229409072119">"U၊ horn နှင့် tilde"</string> - <string name="spoken_accented_letter_1EF1" msgid="1912816350401931115">"U၊ horn နှင့် အောက်မှာ အစက်"</string> + <string name="spoken_accented_letter_1EE9" msgid="4219119671251485651">"U၊ horn နှင့် စူးရှသော"</string> + <string name="spoken_accented_letter_1EEB" msgid="4086009841269002231">"U၊ horn နှင့် တည်ငြိမ်သော"</string> + <string name="spoken_accented_letter_1EED" msgid="3528151733528719847">"U၊ horn နှင့် အပေါ်မှာ ချိတ်"</string> + <string name="spoken_accented_letter_1EEF" msgid="3508548229409072119">"U၊ horn နှင့် tilde"</string> + <string name="spoken_accented_letter_1EF1" msgid="1912816350401931115">"U၊ horn နှင့် အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EF3" msgid="7211760439435946494">"Y၊ တည်ငြိမ်သော"</string> <string name="spoken_accented_letter_1EF5" msgid="8998864482764007384">"Y၊ အောက်မှာ အစက်"</string> <string name="spoken_accented_letter_1EF7" msgid="922043627252869200">"Y၊ အပေါ်မှာ ချိတ်"</string> diff --git a/java/res/values-my-rMM/strings-talkback-descriptions.xml b/java/res/values-my-rMM/strings-talkback-descriptions.xml index a4f84a27a..08fd19014 100644 --- a/java/res/values-my-rMM/strings-talkback-descriptions.xml +++ b/java/res/values-my-rMM/strings-talkback-descriptions.xml @@ -79,7 +79,7 @@ <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"စိတ်ကုန်နေသော မျက်နှာ"</string> <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"ကသိကအောက် မျက်နှာ"</string> <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"နေကာမျက်မှန်တပ် မျက်နှာ"</string> - <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"အံ့အားသင့်နေသော မျက်နှာ"</string> + <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"အံ့အားသင့်နေသော မျက်နှာ"</string> <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"နမ်းနေသော မျက်နှာ"</string> <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"မှုန်ကုပ်ကုပ် မျက်နှာ"</string> <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"အစားထိုးစရာ စာလုံးများ ရှိနိုင်"</string> diff --git a/java/res/values-my-rMM/strings.xml b/java/res/values-my-rMM/strings.xml index 93a74c5a9..ab4037861 100644 --- a/java/res/values-my-rMM/strings.xml +++ b/java/res/values-my-rMM/strings.xml @@ -30,11 +30,11 @@ <string name="settings_screen_accounts" msgid="7570397912370223287">"အကောင့်များ & ကိုယ်ပိုင်ကိစ္စ"</string> <string name="settings_screen_appearance" msgid="9153102634339912029">"အပြင်ပန်း & အပြင်အဆင်များ"</string> <string name="settings_screen_multilingual" msgid="1391301621464509659">"ဘာသာစကားစုံ ရွေးချယ်စရာများ"</string> - <string name="settings_screen_gesture" msgid="8826372746901183556">"လှုပ်ရှားမှုဖြင့်စာရိုက်ခြင်း"</string> + <string name="settings_screen_gesture" msgid="8826372746901183556">"လှုပ်ရှားမှုဖြင့်စာရိုက်ခြင်း"</string> <string name="settings_screen_correction" msgid="1616818407747682955">"စာအမှားပြပြင်ခြင်း"</string> - <string name="settings_screen_advanced" msgid="7472408607625972994">"အဆင့်မြင့်"</string> + <string name="settings_screen_advanced" msgid="7472408607625972994">"အဆင့်မြင့်"</string> <string name="settings_screen_theme" msgid="2137262503543943871">"အပြင်အဆင်"</string> - <string name="enable_split_keyboard" msgid="4177264923999493614">"ကီးဘုတ် ခွဲခြမ်းမှု ဖွင့်ထားရန်"</string> + <string name="enable_split_keyboard" msgid="4177264923999493614">"ကီးဘုတ် ခွဲခြမ်းမှု ဖွင့်ထားရန်"</string> <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"အခြားထည့်သွင်းမည့် နည်းလမ်းများသို့ ပြောင်းရန်"</string> <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"ဘာသာပြောင်းသည့် ကီးသည် အခြားထည့်သွင်းရန် နည်းလမ်းများလည်း ပါဝင်သည်"</string> <string name="show_language_switch_key" msgid="5915478828318774384">"ဘာသာစကား ပြောင်းခလုတ်"</string> @@ -47,7 +47,7 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"အဆယ်ကသွယ်အမည်များ အကြံပြုမည်"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"အကြံပြုချက်များနှင့် အမှားပြင်ခြင်းများအတွက် အဆက်သွယ်မှ အမည်များ အသုံးပြုမည်"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"ကိုယ်ရေးကိုယ်တာ အကြံပြုချက်များ"</string> - <string name="enable_metrics_logging" msgid="5506372337118822837">"မြှင့်တင်ပါ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> + <string name="enable_metrics_logging" msgid="5506372337118822837">"မြှင့်တင်ပါ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="use_double_space_period" msgid="8781529969425082860">"နှစ်နေရာခြား အဆုံးသတ်"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"အချိန်ကာလ"</string> <string name="auto_cap" msgid="1719746674854628252">"အော်တိုစာလုံးကြီးပြောင်း"</string> @@ -84,7 +84,7 @@ <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"သိမ်းရန် ဤနေရာကို ထိပါ"</string> <string name="has_dictionary" msgid="6071847973466625007">"အဘိဓါန်ရနိုင်"</string> <string name="keyboard_layout" msgid="8451164783510487501">"ကီးဘုတ်အရောင်"</string> - <string name="switch_accounts" msgid="3321216593719006162">"အကောင့်များကို ပြောင်းရန်"</string> + <string name="switch_accounts" msgid="3321216593719006162">"အကောင့်များကို ပြောင်းရန်"</string> <string name="no_accounts_selected" msgid="2073821619103904330">"အကောင့်များ မရွေးရသေးပါ"</string> <string name="account_selected" msgid="2846876462199625974">"<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>အား လတ်တလော သုံးနေ၏"</string> <string name="account_select_ok" msgid="9141195141763227797">"အိုကေ"</string> @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ဟင်ဂလိပ် (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ဆားဘီယား (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ရိုးရာ)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ကျစ်လစ်သော)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ဘာသာစကားမရှိ (ဗျည်းအက္ခရာ)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ဗျည်းအက္ခရာ (ကွာတီ)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ဗျည်းအက္ခရာ (ကွာတီ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"တူညီသည့် ထည့်သွင်းရန် စတိုင်လ် ရှိပြီးဖြစ်: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ခလုတ်နှိပ်တုန်ခါမှု ကြာမြင့်ချိန်"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ခလုတ်နှိပ်သည့် အသံအတိုးကျယ်"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ကီးကြာမြင့်စွာ ဖိနှိပ်မှုနှုန်း"</string> <string name="button_default" msgid="3988017840431881491">"ပုံသေ"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> မှကြိုဆိုပါသည်"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"အမူယာ ရိုက်ခြင်းဖြင့်"</string> @@ -154,7 +156,7 @@ <string name="default_user_dict_pref_name" msgid="1625055720489280530">"သုံးစွဲသူ၏ အဘိဓာန်"</string> <string name="dictionary_available" msgid="4728975345815214218">"အဘိဓါန်ရရှိနိုင်"</string> <string name="dictionary_downloading" msgid="2982650524622620983">"လက်ရှိ ဒေါင်းလုပ်လုပ်နေသည်"</string> - <string name="dictionary_installed" msgid="8081558343559342962">"ထည့်သွင်းပြီး"</string> + <string name="dictionary_installed" msgid="8081558343559342962">"ထည့်သွင်းပြီး"</string> <string name="dictionary_disabled" msgid="8950383219564621762">"ထည့်သွင်းထားပြီး၊ ပိတ်ထားသည်"</string> <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"အဘိဓါန်ဝန်ဆောင်မှုသို့ ချိတ်ဆက်ရန် ပြဿနာရှိနေသည်"</string> <string name="no_dictionaries_available" msgid="8039920716566132611">"အဘိဓါန်မရှိ"</string> @@ -168,7 +170,7 @@ <string name="install_dict" msgid="180852772562189365">"တပ်ဆင်ပါ"</string> <string name="cancel_download_dict" msgid="7843340278507019303">"ထားတော့"</string> <string name="delete_dict" msgid="756853268088330054">"ဖျက်ရန်"</string> - <string name="should_download_over_metered_prompt" msgid="1583881200688185508">"သင့်ဖုန်းရှိ ရွေးချယ်ထားသည့် ဘာသာအတွက် အဘိဓါန်ရှိပါသည်။ <br/> အဘိဓါန်အား <b>ဒေါင်းလုပ်လုပ်ကာ</b> the <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> သင့်စာရိုက် အတွေ့အကြုံတိုးတက်စေရန် ကျွန်ုပ်တို့အကြံပြုပါသည်။ <br/> <br/> ဒေါင်းလုပ်လုပ်ရန် 3G ပေါ်တွင် ၁ မှ ၂ မိနစ်ခန့်ကြာနိုင်သည်။ သင့်တွင် <b>အကန့်သတ်မှရိ အချက်လက် သုံးစွဲမှု</b>မရှိလျှင် ငွေကျသင့်နိုင်ပါသည်။ <br/> သင့်တွင် မည်သည့်အချက်လက်သုံးစွဲမှု ရှိနေသည်ကိုမသိလျှင်၊ အလိုအလျောက် ဒေါင်းလုပ်လုပ်ရန် Wi-Fi ကွန်ရက်တစ်ခု ရှာဖွေရန် တိုက်တွန်းပါသည်။ <br/> <br/> နည်းလမ်း: သင့်ဖုန်းကိရိယာရှိ <b>ဆက်တင်ထဲတွင်</b> <b>ဘာသာ & စာရိုက်ထည့်မှု</b> သို့သွားကာ အဘိဓါန်များကို ဒေါင်းလုပ်လုပ်နိုင် ဖယ်ရှားနိုင်ပါသည်။"</string> + <string name="should_download_over_metered_prompt" msgid="1583881200688185508">"သင့်ဖုန်းရှိ ရွေးချယ်ထားသည့် ဘာသာအတွက် အဘိဓါန်ရှိပါသည်။ <br/> အဘိဓါန်အား <b>ဒေါင်းလုပ်လုပ်ကာ</b> the <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> သင့်စာရိုက် အတွေ့အကြုံတိုးတက်စေရန် ကျွန်ုပ်တို့အကြံပြုပါသည်။ <br/> <br/> ဒေါင်းလုပ်လုပ်ရန် 3G ပေါ်တွင် ၁ မှ ၂ မိနစ်ခန့်ကြာနိုင်သည်။ သင့်တွင် <b>အကန့်သတ်မှရိ အချက်လက် သုံးစွဲမှု</b>မရှိလျှင် ငွေကျသင့်နိုင်ပါသည်။ <br/> သင့်တွင် မည်သည့်အချက်လက်သုံးစွဲမှု ရှိနေသည်ကိုမသိလျှင်၊ အလိုအလျောက် ဒေါင်းလုပ်လုပ်ရန် Wi-Fi ကွန်ရက်တစ်ခု ရှာဖွေရန် တိုက်တွန်းပါသည်။ <br/> <br/> နည်းလမ်း: သင့်ဖုန်းကိရိယာရှိ <b>ဆက်တင်ထဲတွင်</b> <b>ဘာသာ & စာရိုက်ထည့်မှု</b> သို့သွားကာ အဘိဓါန်များကို ဒေါင်းလုပ်လုပ်နိုင် ဖယ်ရှားနိုင်ပါသည်။"</string> <string name="download_over_metered" msgid="1643065851159409546">"ယခုဒေါင်းလုပ်လုပ်မည် (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string> <string name="do_not_download_over_metered" msgid="2176209579313941583">"Wi-Fi အသုံးပြု၍ ဒေါင်းလုပ်လုပ်ရန်"</string> <string name="dict_available_notification_title" msgid="4583842811218581658">"<xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> အတွက် အဘိဓါန် ရနိုင်ပါသည်"</string> @@ -189,7 +191,7 @@ <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"စာလုံးကို ပြင်ဆင်မည်"</string> <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"တည်းဖြတ်ရန်"</string> <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"ဖျက်ရန်"</string> - <string name="user_dict_settings_empty_text" msgid="558499587532668203">"သင့်အဘိဓာန်ထဲတွင် မည်သည့်စာလုံးမှမရှိပါ။ ထပ်ထည့်ခြင်း(+)ခလုတ်ကို ထိ၍ စာလုံးထည့်ပါ။"</string> + <string name="user_dict_settings_empty_text" msgid="558499587532668203">"သင့်အဘိဓာန်ထဲတွင် မည်သည့်စာလုံးမှမရှိပါ။ ထပ်ထည့်ခြင်း(+)ခလုတ်ကို ထိ၍ စာလုံးထည့်ပါ။"</string> <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"ဘာသာစကားအားလုံးအတွက်"</string> <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"ဘာသာစကားပိုများများ…"</string> <string name="user_dict_settings_delete" msgid="110413335187193859">"ဖျက်သိမ်းရန်"</string> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 268d76a25..849d7ab2d 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisk (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradisjonelt)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Ingen språk (alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Inndatastilen finnes allerede: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasjonstid ved tastetrykk"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Lydstyrke ved tastetrykk"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Forsinkelse lange tastetrykk"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Velkommen til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med Ordføring"</string> diff --git a/java/res/values-ne-rNP/strings-emoji-descriptions.xml b/java/res/values-ne-rNP/strings-emoji-descriptions.xml index 43f4d892b..39e5bc075 100644 --- a/java/res/values-ne-rNP/strings-emoji-descriptions.xml +++ b/java/res/values-ne-rNP/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"कुकीज"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"चकलेट बार"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"क्यान्डी"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"लालीपप"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"कस्तार्ड"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"महदानी"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"सर्टकेक"</string> diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml index 9f486fdb4..40c2d4f96 100644 --- a/java/res/values-ne-rNP/strings.xml +++ b/java/res/values-ne-rNP/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिङ्लिस (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (परम्परागत)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (संकुचित)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"कुनै भाषा होइन (वर्णमाला)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"यस्तो इनपुट शैली पहिले नै अवस्थित छ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुञ्जी थिचाइ भाइब्रेसन अवधि"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुञ्जी थिचाइ आवाज भोल्युम"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुञ्जी लामो थिचाइ ढिलाइ"</string> <string name="button_default" msgid="3988017840431881491">"पूर्वनिर्धारित"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"तपाईँलाई स्वागत छ<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"इशारा टाइप गर्नेसँग"</string> diff --git a/java/res/values-nl/strings-emoji-descriptions.xml b/java/res/values-nl/strings-emoji-descriptions.xml index 3b298900b..a02c21fa0 100644 --- a/java/res/values-nl/strings-emoji-descriptions.xml +++ b/java/res/values-nl/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Cookie"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Chocoladereep"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Snoep"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lolly"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vla"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honingpot"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Cake"</string> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index ec5fa06f8..d09db3f17 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi-Engels (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Servisch (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditioneel)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compact)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal (alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Dezelfde invoerstijl bestaat al: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trilingsduur bij toetsgebruik"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Geluidsvolume bij toetsgebruik"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Vertraging toets lang indrukkn"</string> <string name="button_default" msgid="3988017840431881491">"Standaard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welkom bij <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"met Invoer met bewegingen"</string> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 0bc3d4b15..39cf7422d 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradycyjny)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktowa)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Bez języka (alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Taki styl wprowadzania już istnieje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Wibracja przy naciśniętym klawiszu"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Głośność przy naciśniętym klawiszu"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Opóźnienie przy długim naciśnięciu"</string> <string name="button_default" msgid="3988017840431881491">"Domyślne"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Witamy w aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"z pisaniem gestami"</string> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 2c7ff8b40..e063cba31 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sérvio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Sem idioma (alfabeto)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Já existe o mesmo estilo de introdução: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duração vibr. ao premir teclas"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume do som ao premir teclas"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Atraso ao manter tecla premida"</string> <string name="button_default" msgid="3988017840431881491">"Predefinido"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bem-vindo(a) a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"com a Escrita com Gestos"</string> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index f74072228..2e0cd3b55 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -102,6 +102,8 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Híndi-inglês (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sérvio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string> + <!-- no translation found for subtype_generic_compact (3353673321203202922) --> + <skip /> <string name="subtype_no_language" msgid="7137390094240139495">"Nenhum idioma (alfabeto)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string> @@ -124,6 +126,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"O estilo de entrada já existe: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duração da vibração ao tocar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume ao tocar na tela"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Atraso ao pressionar teclas"</string> <string name="button_default" msgid="3988017840431881491">"Padrão"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bem-vindo ao <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"com entrada por gestos"</string> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 007c61dfc..03398733c 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sârbă (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradițională)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Nicio limbă (alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Acelaşi stil de introducere există deja: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrare după apăsarea tastei"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Sunet la apăsarea tastelor"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Timpul apăsării lungi a tastei"</string> <string name="button_default" msgid="3988017840431881491">"Prestabilit"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bun venit la <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"cu Tastarea gestuală"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 15ddb5ebf..e5aaf8ba7 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сербский (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (классическая)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактная раскладка)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Язык не определен (латиница)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Такой стиль ввода уже существует: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Вибросигнал при нажатии клавиш"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Звук при нажатии клавиш"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Долгое нажатие"</string> <string name="button_default" msgid="3988017840431881491">"По умолчанию"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Представляем приложение \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\""</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"с непрерывным вводом"</string> diff --git a/java/res/values-si-rLK/strings.xml b/java/res/values-si-rLK/strings.xml index fc69c80df..f8fa5e7c3 100644 --- a/java/res/values-si-rLK/strings.xml +++ b/java/res/values-si-rLK/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"හින්ග්ලිෂ් (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"සර්බියානු (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (සාම්ප්රදායික)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (සංයුක්ත)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"භාෂාවක් නැත (අකාරාදිය)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"අකාරාදිය (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"අකාරාදිය (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"සමාන ආදාන විලාසය දැනටමත් පවතී: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"යතුරු එබිම් කම්පන කාලපරිච්ඡේදය"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"යතුරු එබීම් හඬ තීව්රතාවය"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"යතුරු දිගු එබීම් ප්රමාදය"</string> <string name="button_default" msgid="3988017840431881491">"සුපුරුදු"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> වෙත සාදරයෙන් පිළිගනිමු"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ඉංගිත ටයිප් කිරීම් සමග"</string> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 2f428aee6..0d0e69fee 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srbčina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradičná)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktná)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Žiadny jazyk (latinka)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Rovnaký štýl vstupu už existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trvanie vibrov. pri stlač. kl."</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Hlasitosť pri stlačení klávesu"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Oneskor. pri stlač. a podržaní"</string> <string name="button_default" msgid="3988017840431881491">"Predvolené"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Vitajte v aplikácii <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s funkciou Písanie gestami"</string> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index 57d5d7268..e1f86c36c 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindujska angleščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srbščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalna)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktna)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Brez jezika (latinice)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinica (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinica (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Isti slog vnosa že obstaja: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trajanje vibr. ob prit. tipke"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Glasn. zvoka ob pritisku tipke"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Zakasn. za dolg pritisk tipke"</string> <string name="button_default" msgid="3988017840431881491">"Privzeto"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Pozdravljeni v aplikaciji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s pisanjem s kretnjami"</string> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index de2add40a..f8c4ff601 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -95,13 +95,14 @@ <string name="subtype_en_US" msgid="6160452336634534239">"енглески (САД)"</string> <string name="subtype_es_US" msgid="5583145191430180200">"шпански (САД)"</string> <string name="subtype_hi_ZZ" msgid="8860448146262798623">"хенглески"</string> - <string name="subtype_sr_ZZ" msgid="9059219552986034343">"srpski"</string> + <string name="subtype_sr_ZZ" msgid="9059219552986034343">"српски (латиница)"</string> <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"енглески (УК) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"енглески (САД) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_es_US" msgid="510930471167541338">"шпански (САД) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"хенглески (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> - <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> + <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"српски (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционални)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактна)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Нема језика (абецеда)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Абецеда (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Абецеда (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Исти стил уноса већ постоји: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Трајање вибрације при притиску"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Јачина звука при притиску"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Одлагање при дугом притиску"</string> <string name="button_default" msgid="3988017840431881491">"Подразумевано"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Добро дошли у <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"помоћу Куцања покретима"</string> diff --git a/java/res/values-sv/strings-emoji-descriptions.xml b/java/res/values-sv/strings-emoji-descriptions.xml index 879de0b90..e49c73ce4 100644 --- a/java/res/values-sv/strings-emoji-descriptions.xml +++ b/java/res/values-sv/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Småkaka"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Chokladkaka"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Godis"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Klubba"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vaniljkräm"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honungsburk"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Sockerkaka"</string> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index f7a94d92b..0c7d1a766 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi/engelska (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbiska (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionell)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Inget språk (alfabet)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Samma indatastil finns redan: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationslängd för tangenter"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ljudvolym för tangenter"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Fördröjning vid långt tryck"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Välkommen till <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med svepskrivning"</string> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 5f86d351b..04c4f2bb7 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Kiserbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cha Jadi)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Thabiti)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Hakuna lugha (Alfabeti)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeti (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeti (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Mfumo sawa wa maingizo tayari upo: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Bonyeza kitufe cha muda wa kutetema"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Bonyeza kitufe cha kiwango cha sauti"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ubofyaji kitufe kunakochelewa"</string> <string name="button_default" msgid="3988017840431881491">"Chaguo-msingi"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Karibu kwenye <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kwa Kuandika kwa ishara"</string> diff --git a/java/res/values-ta-rIN/strings.xml b/java/res/values-ta-rIN/strings.xml index d99d8c7ee..64c573ba8 100644 --- a/java/res/values-ta-rIN/strings.xml +++ b/java/res/values-ta-rIN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ஹிங்கிலிஷ் (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"செர்பியன் (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (பாரம்பரியமானது)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (வசதியான)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"மொழியில்லை (அகரவரிசை)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"அகரவரிசை (க்வெர்டி)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"அகரவரிசை (க்வெர்ட்ச்)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"இதே உள்ளீட்டு நடை ஏற்கனவே உள்ளது: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"விசையழுத்த அதிர்வின் காலஅளவு"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"விசையழுத்த ஒலியளவு"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"விசையின் நீண்ட அழுத்தத்தின் தாமதம்"</string> <string name="button_default" msgid="3988017840431881491">"இயல்புநிலை"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> பயன்பாட்டிற்கு வரவேற்கிறோம்"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"சைகை உள்ளீடு மூலம்"</string> diff --git a/java/res/values-te-rIN/strings.xml b/java/res/values-te-rIN/strings.xml index 7fca66125..0fb55b4b2 100644 --- a/java/res/values-te-rIN/strings.xml +++ b/java/res/values-te-rIN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"హింగ్లీష్ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"సెర్బియన్ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (సాంప్రదాయకం)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (కాంపాక్ట్)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"భాష లేదు (ఆల్ఫాబెట్)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ఆల్ఫాబెట్ (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ఆల్ఫాబెట్ (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ఇదే ఇన్పుట్ శైలి ఇప్పటికే ఉంది: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"కీని నొక్కినప్పుడు వైబ్రేషన్ వ్యవధి"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"కీని నొక్కినప్పుడు చేసే ధ్వని వాల్యూమ్"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"కీని ఎక్కువసేపు నొక్కి ఉంచాల్సిన సమయంలో ఆలస్యం"</string> <string name="button_default" msgid="3988017840431881491">"డిఫాల్ట్"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>కు స్వాగతం"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"సంజ్ఞ టైపింగ్తో"</string> diff --git a/java/res/values-th/strings-emoji-descriptions.xml b/java/res/values-th/strings-emoji-descriptions.xml index 86ab2c08e..e5ef9b89d 100644 --- a/java/res/values-th/strings-emoji-descriptions.xml +++ b/java/res/values-th/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"คุกกี้"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ช็อกโกแลตแท่ง"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ลูกกวาด"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"อมยิ้ม"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"คัสตาร์ด"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"โถน้ำผึ้ง"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ชอร์ตเค้ก"</string> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index 94374cf1a..fcc76d9b9 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ภาษาอังกฤษผสมกับฮินดู (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"เซอร์เบีย (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ดั้งเดิม)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (แบบกะทัดรัด)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"ไม่มีภาษา (ตัวอักษรละติน)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ตัวอักษร (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ตัวอักษร (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"รูปแบบการป้อนข้อมูลเดียวกันนี้มีอยู่แล้ว: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ระยะเวลาการสั่นเมื่อกดแป้นพิมพ์"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ระดับเสียงเมื่อกดแป้นพิมพ์"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"การหน่วงเวลาของการกดแป้นค้าง"</string> <string name="button_default" msgid="3988017840431881491">"ค่าเริ่มต้น"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"ยินดีต้อนรับสู่ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"พร้อมการป้อนข้อมูลด้วยท่าทาง"</string> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index d523e2e46..82617b889 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Walang wika (Alpabeto)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alpabeto (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alpabeto (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Umiiral na ang parehong estilo ng input: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tagal ng vibration ng keypress"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume ng tunog ng keypress"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Maligayang pagdating sa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"gamit ang Gesture na Pag-type"</string> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 1db5ec182..359e0d16f 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilizce (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sırpça (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Geleneksel)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompakt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Dil yok (Alfabe)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabe (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabe (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Aynı giriş stili zaten var: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tuşa basma titreşim süresi"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tuşa basma ses seviyesi"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Tuşa uzun basma gecikmesi"</string> <string name="button_default" msgid="3988017840431881491">"Varsayılan"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> uygulamasına hoş geldiniz"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Hareketle Yazmayı içerir"</string> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 29003d1be..dd625cb70 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хінґліш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"сербська (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиційна)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактна)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Стандартна (латиниця)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиниця (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиниця (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Такий стиль введення вже існує: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Вібрація при натисканні клавіш"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Гучність натискання клавіш"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Затримка довгого натискання"</string> <string name="button_default" msgid="3988017840431881491">"За умовчанням"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Вітаємо в програмі <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"з функцією Ввід жестами"</string> diff --git a/java/res/values-ur-rPK/strings-emoji-descriptions.xml b/java/res/values-ur-rPK/strings-emoji-descriptions.xml index e6bbdcf49..d5bcf2f94 100644 --- a/java/res/values-ur-rPK/strings-emoji-descriptions.xml +++ b/java/res/values-ur-rPK/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"کوکی"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"چاکلیٹ بار"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"قندی"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"لالی پاپ"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"کسٹرڈ"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"شہد کا برتن"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"کیک کا ٹکڑا"</string> diff --git a/java/res/values-ur-rPK/strings.xml b/java/res/values-ur-rPK/strings.xml index 041192e85..4b1f03b03 100644 --- a/java/res/values-ur-rPK/strings.xml +++ b/java/res/values-ur-rPK/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ہنگلش (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"سربیائی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (روایتی)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (کمپیکٹ)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"کوئی زبان نہیں (الفابیٹ)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"حروف تہجی (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"حروف تہجی (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ایسا ہی ان پٹ اسٹائل پہلے سے موجود ہے: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"کلید دبانے پر وائبریشن کا دورانیہ"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"کلید دبانے پر آواز کا والیوم"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"کلید کو دیر تک دبانے کی تاخیر"</string> <string name="button_default" msgid="3988017840431881491">"ڈیفالٹ"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> میں خوش آمدید"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"اشارہ ٹائپنگ کے ساتھ"</string> diff --git a/java/res/values-uz-rUZ/strings.xml b/java/res/values-uz-rUZ/strings.xml index d3fcd20de..59aba60f0 100644 --- a/java/res/values-uz-rUZ/strings.xml +++ b/java/res/values-uz-rUZ/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (an’anaviy)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ixcham)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Til aniqlanmadi (lotin)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Lotin (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Lotin (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Xuddi shunday matn kiritish usuli allaqachon mavjud: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tugma bosilganda tebranish vaqti"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tugma bosilgandagi ovoz"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Uzoq bosib turish"</string> <string name="button_default" msgid="3988017840431881491">"Standart"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> ilovasiga xush kelibsiz!"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Imo-ishoralar bilan yozish"</string> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 08d5eee17..ec0deb117 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -25,7 +25,7 @@ <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Trình kiểm tra chính tả sử dụng các mục nhập từ danh sách liên hệ của bạn"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rung khi nhấn phím"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Âm thanh khi nhấn phím"</string> - <string name="popup_on_keypress" msgid="123894815723512944">"Cửa sổ bật lên khi nhấn phím"</string> + <string name="popup_on_keypress" msgid="123894815723512944">"Bật lên khi nhấn phím"</string> <string name="settings_screen_preferences" msgid="2696713156722014624">"Tùy chọn"</string> <string name="settings_screen_accounts" msgid="7570397912370223287">"Tài khoản và bảo mật"</string> <string name="settings_screen_appearance" msgid="9153102634339912029">"Giao diện và bố cục"</string> @@ -48,8 +48,8 @@ <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Sử dụng tên từ Danh bạ cho các đề xuất và chỉnh sửa"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Đề xuất được cá nhân hóa"</string> <string name="enable_metrics_logging" msgid="5506372337118822837">"Cải thiện <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="use_double_space_period" msgid="8781529969425082860">"Dấu cách đôi"</string> - <string name="use_double_space_period_summary" msgid="6532892187247952799">"Nhấn đúp vào phím cách sẽ chèn thêm một dấu sau dấu cách"</string> + <string name="use_double_space_period" msgid="8781529969425082860">"Nhấn đúp phím cách chèn dấu chấm câu"</string> + <string name="use_double_space_period_summary" msgid="6532892187247952799">"Nhấn đúp phím cách sẽ chèn thêm một dấu chấm câu, theo sau là dấu cách"</string> <string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string> <string name="auto_cap_summary" msgid="7934452761022946874">"Viết hoa chữ đầu tiên của mỗi câu"</string> <string name="edit_personal_dictionary" msgid="3996910038952940420">"Từ điển cá nhân"</string> @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Tiếng Anh-Hindi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Tiếng Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Truyền thống)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Viết tắt)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Không ngôn ngữ nào (Bảng chữ cái)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Bảng chữ cái (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Bảng chữ cái (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Đã tồn tại kiểu nhập tương tự: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Thời gian rung khi nhấn phím"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Âm lượng khi nhấn phím"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Tgian chờ cho nhấn và giữ phím"</string> <string name="button_default" msgid="3988017840431881491">"Mặc định"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Chào mừng bạn đến với <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"với Nhập bằng cử chỉ"</string> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index 7e0a103f4..52b0dac74 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印地英语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞尔维亚语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>布局)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(传统)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(紧凑型)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"无语言(字母)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已经存在相同的输入风格:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按键振动时长"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按键音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"按键长按延迟"</string> <string name="button_default" msgid="3988017840431881491">"默认"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"欢迎使用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"体验顺畅的滑行输入体验"</string> diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml index 74a552421..840f33387 100644 --- a/java/res/values-zh-rHK/strings.xml +++ b/java/res/values-zh-rHK/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (精簡版)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已存在相同的輸入樣式:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按鍵震動時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按鍵音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"長按鍵延遲"</string> <string name="button_default" msgid="3988017840431881491">"預設"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"歡迎使用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"配備觸控輸入功能"</string> diff --git a/java/res/values-zh-rTW/strings-emoji-descriptions.xml b/java/res/values-zh-rTW/strings-emoji-descriptions.xml index 06e260a69..b5c723066 100644 --- a/java/res/values-zh-rTW/strings-emoji-descriptions.xml +++ b/java/res/values-zh-rTW/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"餅乾"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"巧克力棒"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"糖果"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"棒棒糖"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"卡士達"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"蜂蜜罐"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"水果蛋糕"</string> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 3b999eaf5..8fac3105e 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (精簡)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已存在相同的輸入樣式:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按鍵震動持續時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按鍵音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"按鍵長按延遲"</string> <string name="button_default" msgid="3988017840431881491">"預設"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"歡迎使用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"含滑行輸入功能"</string> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index 4d2afab9b..0177129dc 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -102,6 +102,7 @@ <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"I-Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Isi-Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string> <string name="subtype_generic_traditional" msgid="8584594350973800586">"Isi-<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradition)"</string> + <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Okuqoqene ndawonye)"</string> <string name="subtype_no_language" msgid="7137390094240139495">"Alikho ulimi (Alfabhethi)"</string> <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabhethi (QWERTY)"</string> <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabhethi (QWERTZ)"</string> @@ -124,6 +125,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Isitayela sokufaka esifanayo sesivele sikhona: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Ubude besikhathi sokudlidliza ukucindezela ukhiye"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ivolumu yomsindo wokucindezela ukhiye"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ukulibazisa ukucindezela isikhashana ukhiye"</string> <string name="button_default" msgid="3988017840431881491">"Okuzenzakalelayo"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Siyakwamukela ku-<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"nokuthayipha ngokuthinta"</string> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index be35d131d..bf36337a1 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -43,15 +43,18 @@ </declare-styleable> <declare-styleable name="KeyboardView"> - <!-- Background image for the key. This image needs to be a {@link StateListDrawable}, - with the following possible states: normal, pressed, checkable, checkable+pressed, - checkable+checked, checkable+checked+pressed. --> + <!-- Background image for the key. This image needs to be a + {@link android.graphics.drawable.StateListDrawable}, with the following possible states: + normal, pressed, checkable, checkable+pressed, checkable+checked, + checkable+checked+pressed. --> <attr name="keyBackground" format="reference" /> <!-- Background image for the functional key. This image needs to be a - {@link StateListDrawable}, with the following possible states: normal, pressed. --> + {@link android.graphics.drawable.StateListDrawable}, with the following possible + states: normal, pressed. --> <attr name="functionalKeyBackground" format="reference" /> <!-- Background image for the spacebar. This image needs to be a - {@link StateListDrawable}, with the following possible states: normal, pressed. --> + {@link android.graphics.drawable.StateListDrawable}, with the following possible + states: normal, pressed. --> <attr name="spacebarBackground" format="reference" /> <attr name="spacebarIconWidthRatio" format="float" /> <!-- Right padding of hint letter to the edge of the key.--> @@ -200,7 +203,8 @@ <declare-styleable name="SuggestionStripView"> <attr name="suggestionStripOptions" format="integer"> - <!-- This should be aligned with SuggestionStripLayoutHelper.AUTO_CORRECT_* and etc. --> + <!-- This should be aligned with + {@link com.android.inputmethod.latin.suggestions.SuggestionStripLayoutHelper.AUTO_CORRECT_BOLD} etc. --> <flag name="autoCorrectBold" value="0x01" /> <flag name="autoCorrectUnderline" value="0x02" /> <flag name="validTypedWordBold" value="0x04" /> @@ -218,7 +222,8 @@ <declare-styleable name="Keyboard"> <attr name="themeId" format="enum"> - <!-- This should be aligned with KeyboardTheme.THEME_ID_*. --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.KeyboardTheme#THEME_ID_ICS} etc. --> <enum name="ICS" value="0" /> <enum name="KLP" value="2" /> <enum name="LXXLight" value="3" /> @@ -240,8 +245,8 @@ <attr name="verticalGap" format="fraction" /> <!-- More keys keyboard layout template --> <attr name="moreKeysTemplate" format="reference" /> - <!-- Icon set for key top and key preview. - These should be aligned with KeyboardIconsSet.NAMES_AND_ATTR_IDS[] --> + <!-- Icon set for key top and key preview. These should be aligned with + {@link com.android.inputmethod.keyboard.internal.KeyboardIconsSet#NAMES_AND_ATTR_IDS} --> <attr name="iconShiftKey" format="reference" /> <attr name="iconDeleteKey" format="reference" /> <attr name="iconSettingsKey" format="reference" /> @@ -287,7 +292,8 @@ <!-- Maximum column of more keys keyboard --> <attr name="maxMoreKeysColumn" format="integer" /> <attr name="backgroundType" format="enum"> - <!-- This should be aligned with Key.BACKGROUND_TYPE_* --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.Key#BACKGROUND_TYPE_NORMAL} etc. --> <enum name="empty" value="0" /> <enum name="normal" value="1" /> <enum name="functional" value="2" /> @@ -298,7 +304,8 @@ </attr> <!-- The key action flags. --> <attr name="keyActionFlags" format="integer"> - <!-- This should be aligned with Key.ACTION_FLAGS_* --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.Key#ACTION_FLAGS_IS_REPEATABLE} etc. --> <flag name="isRepeatable" value="0x01" /> <flag name="noKeyPreview" value="0x02" /> <flag name="altCodeWhileTyping" value="0x04" /> @@ -312,7 +319,8 @@ <attr name="keyHintLabelVerticalAdjustment" format="fraction" /> <!-- The key label flags. --> <attr name="keyLabelFlags" format="integer"> - <!-- This should be aligned with Key.LABEL_FLAGS__* --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.Key#LABEL_FLAGS_FONT_NORMAL} etc. --> <flag name="alignHintLabelToBottom" value="0x02" /> <flag name="alignIconToBottom" value="0x04" /> <flag name="alignLabelOffCenter" value="0x08" /> @@ -357,7 +365,8 @@ <!-- Width of the key, in the proportion of keyboard width. If the value is fillRight, the actual key width will be determined to fill out the area up to the right edge of the keyboard. --> - <!-- This should be aligned with KeyboardBuilder.Row.KEYWIDTH_* --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.internal.KeyboardRow#KEYWIDTH_FILL_RIGHT}. --> <attr name="keyWidth" format="fraction|enum"> <enum name="fillRight" value="-1" /> </attr> @@ -368,7 +377,7 @@ <!-- Key top visual attributes --> <attr name="keyTypeface" format="enum"> - <!-- This should be aligned with Typeface.NORMAL etc. --> + <!-- This should be aligned with {@link android.graphics.Typeface#NORMAL} etc. --> <enum name="normal" value="0" /> <enum name="bold" value="1" /> <enum name="italic" value="2" /> @@ -391,11 +400,11 @@ <attr name="keyShiftedLetterHintRatio" format="fraction" /> <!-- The label's horizontal offset to the center of the key. Negative is to left and positive is to right. The value is in proportion of the width of - TypefaceUtils.KEY_LABEL_REFERENCE_CHAR. --> + {@link com.android.inputmethod.latin.utils.TypefaceUtils#KEY_LABEL_REFERENCE_CHAR}. --> <attr name="keyLabelOffCenterRatio" format="fraction" /> <!-- The hint label's horizontal offset to the center of the key. Negative is to left and positive is to right. The value is in proportion of the width of - TypefaceUtils.KEY_LABEL_REFERENCE_CHAR. --> + {@link com.android.inputmethod.latin.utils.TypefaceUtils#KEY_LABEL_REFERENCE_CHAR}. --> <attr name="keyHintLabelOffCenterRatio" format="fraction" /> <!-- Color to use for the label in a key. --> <attr name="keyTextColor" format="color" /> @@ -425,7 +434,8 @@ <declare-styleable name="Keyboard_Case"> <attr name="keyboardLayoutSet" format="string" /> - <!-- This should be aligned with KeyboardLayoutSet_Element's elementName. --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.KeyboardId#ELEMENT_ALPHABET} etc. --> <attr name="keyboardLayoutSetElement" format="enum|string"> <enum name="alphabet" value="0" /> <enum name="alphabetManualShifted" value="1" /> @@ -445,14 +455,16 @@ <enum name="emojiCategory5" value="15" /> <enum name="emojiCategory6" value="16" /> </attr> - <!-- This should be aligned with Keyboard.themeId and KeyboardTheme.THEME_ID_* --> + <!-- This should be aligned with Keyboard.themeId and + {@link com.android.inputmethod.keyboard.KeyboardTheme#THEME_ID_ICS} etc. --> <attr name="keyboardTheme" format="enum|string"> <enum name="ICS" value="0" /> <enum name="KLP" value="2" /> <enum name="LXXLight" value="3" /> <enum name="LXXDark" value="4" /> </attr> - <!-- This should be aligned with KeyboardId.MODE_* --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.KeyboardId#MODE_TEXT} etc. --> <attr name="mode" format="enum|string"> <enum name="text" value="0" /> <enum name="url" value="1" /> @@ -469,7 +481,8 @@ <attr name="languageSwitchKeyEnabled" format="boolean" /> <attr name="isMultiLine" format="boolean" /> <attr name="imeAction" format="enum"> - <!-- This should be aligned with EditorInfo.IME_ACTION_* --> + <!-- This should be aligned with + {@link android.view.inputmethod.EditorInfo#IME_ACTION_GO} etc. --> <enum name="actionUnspecified" value="0" /> <enum name="actionNone" value="1" /> <enum name="actionGo" value="2" /> @@ -478,7 +491,8 @@ <enum name="actionNext" value="5" /> <enum name="actionDone" value="6" /> <enum name="actionPrevious" value="7" /> - <!-- This should be aligned with KeyboardId.IME_ACTION_* --> + <!-- This should be aligned with + {@link com.android.inputmethod.latin.utils.InputTypeUtils#IME_ACTION_CUSTOM_LABEL}. --> <enum name="actionCustomLabel" value="0x100" /> </attr> <attr name="isIconDefined" format="string" /> @@ -495,7 +509,8 @@ </declare-styleable> <declare-styleable name="KeyboardLayoutSet_Element"> - <!-- This should be aligned with KeyboardId.ELEMENT_* --> + <!-- This should be aligned with + {@link com.android.inputmethod.keyboard.KeyboardId#ELEMENT_ALPHABET} etc. --> <attr name="elementName" format="enum"> <enum name="alphabet" value="0" /> <enum name="alphabetManualShifted" value="1" /> @@ -525,7 +540,8 @@ </declare-styleable> <declare-styleable name="KeyboardLayoutSet_Feature"> - <!-- This should be aligned with ScriptUtils.SCRIPT_* --> + <!-- This should be aligned with + {@link com.android.inputmethod.latin.utils.ScriptUtils#SCRIPT_ARABIC} etc. --> <attr name="supportedScript" format="enum"> <enum name="arabic" value="0" /> <enum name="armenian" value="1" /> diff --git a/java/res/values/donottranslate-debug-settings.xml b/java/res/values/donottranslate-debug-settings.xml index 199f9772b..491043f1a 100644 --- a/java/res/values/donottranslate-debug-settings.xml +++ b/java/res/values/donottranslate-debug-settings.xml @@ -22,14 +22,11 @@ <string name="english_ime_debug_settings">Android Keyboard Debug settings</string> <string name="prefs_debug_mode">Debug Mode</string> <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string> - <string name="prefs_force_physical_keyboard_special_key">Force physical keyboard special key</string> <string name="prefs_should_show_lxx_suggestion_ui">Show LXX suggestion UI</string> <!-- Option to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=30]--> <string name="sliding_key_input_preview">Show slide indicator</string> <!-- Option summary to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=66]--> <string name="sliding_key_input_preview_summary">Display visual cue while sliding from Shift or Symbol keys</string> - <!-- Title of the settings for key long press delay [CHAR LIMIT=35] --> - <string name="prefs_key_longpress_timeout_settings">Key long press delay</string> <!-- Title of the settings for customize key popup animation parameters [CHAR LIMIT=35] --> <string name="prefs_customize_key_preview_animation">Customize key preview animation</string> <!-- Title of the settings for key popup show up animation duration (in milliseconds) [CHAR LIMIT=35] --> @@ -46,6 +43,10 @@ <string name="prefs_key_popup_dismiss_end_y_scale_settings">Key popup dismiss end Y scale</string> <!-- Title of the settings for reading an external dictionary file --> <string name="prefs_read_external_dictionary">Read external dictionary file</string> + <!-- Title of the settings to enable keyboard resizing --> + <string name="prefs_resize_keyboard">Enable keyboard resizing</string> + <!-- Title of the settings for setting keyboard height --> + <string name="prefs_keyboard_height_scale">Keyboard height scale</string> <!-- Message to show when there are no files to install as an external dictionary [CHAR LIMIT=100] --> <string name="read_external_dictionary_no_files_message">No dictionary files in the Downloads folder</string> <!-- Title of the dialog that selects a file to install as an external dictionary [CHAR LIMIT=50] --> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index 959fe833b..08d8bb260 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -19,7 +19,7 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- For backward compatibility. - See {@link SettingsValues#needsToShowVoiceInputKey(SharedPreferences,Resources)} --> + @see com.android.inputmethod.latin.settings.SettingsValues#needsToShowVoiceInputKey(android.content.SharedPreferences,android.content.res.Resources) --> <string name="voice_mode_main">0</string> <!-- Subtype locale display name exceptions. @@ -39,6 +39,8 @@ <item>hi_ZZ</item> <item>sr_ZZ</item> </string-array> + <string name="subtype_in_root_locale_hi_ZZ">Hinglish</string> + <string name="subtype_in_root_locale_sr_ZZ">Srpski</string> <!-- Generic subtype label --> <string name="subtype_generic">%s</string> diff --git a/java/res/values/keyboard-themes.xml b/java/res/values/keyboard-themes.xml index b0bae9647..26b258227 100644 --- a/java/res/values/keyboard-themes.xml +++ b/java/res/values/keyboard-themes.xml @@ -25,7 +25,8 @@ <item>@string/keyboard_theme_holo_white</item> <item>@string/keyboard_theme_holo_blue</item> </string-array> - <!-- An element must be a keyboard theme id of {@link KeyboardTheme#THEME_ID_*}. --> + <!-- An element must be a keyboard theme id of + {@link com.android.inputmethod.keyboard.KeyboardTheme#THEME_ID_ICS} etc. --> <integer-array name="keyboard_theme_ids" translatable="false"> <item>3</item> <item>4</item> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index ed05e916f..e1a72c476 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -214,8 +214,7 @@ <!-- Description for Hinglish (https://en.wikipedia.org/wiki/Hinglish) keyboard subtype [CHAR LIMIT=25] --> <string name="subtype_hi_ZZ">Hinglish</string> <!-- Description for Serbian (Latin) keyboard subtype [CHAR LIMIT=25] - (Latin) can be an abbreviation to fit in the CHAR LIMIT. - Note for Serbian translator: this should be translated with Latin script and (Latin) should be omitted. --> + (Latin) can be an abbreviation to fit in the CHAR LIMIT. --> <string name="subtype_sr_ZZ">Serbian (Latin)</string> <!-- Description for English (UK) keyboard subtype with explicit keyboard layout [CHAR LIMIT=25] (UK) should be an abbreviation of United Kingdom to fit in the CHAR LIMIT. @@ -233,16 +232,14 @@ This should be identical to subtype_hi_ZZ aside from the trailing (%s). --> <string name="subtype_with_layout_hi_ZZ">Hinglish (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string> <!-- Description for Serbian (Latin) keyboard subtype with explicit keyboard layout [CHAR LIMIT=25] - This should be identical to subtype_sr_ZZ aside from the trailing (%s). - Note for Serbian translator: this should be translated with Latin script. --> + This should be identical to subtype_sr_ZZ aside from the trailing (%s). --> <string name="subtype_with_layout_sr_ZZ">Serbian (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string> <!-- Description for "LANGUAGE_NAME" (Traditional) keyboard subtype [CHAR LIMIT=25] (Traditional) can be an abbreviation to fit in the CHAR LIMIT. --> <string name="subtype_generic_traditional"><xliff:g id="LANGUAGE_NAME" example="Nepali">%s</xliff:g> (Traditional)</string> <!-- Description for "LANGUAGE_NAME" (Compact) keyboard subtype [CHAR LIMIT=25] - (Compact) can be an abbreviation to fit in the CHAR LIMIT. - TODO: Remove translatable=false once we are settled down with the naming. --> - <string name="subtype_generic_compact" translatable="false"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compact)</string> + (Compact) can be an abbreviation to fit in the CHAR LIMIT. --> + <string name="subtype_generic_compact"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compact)</string> <!-- This string is displayed in a language list that allows to choose a language for suggestions in a software keyboard. This setting won't give suggestions in any particular language, hence "No language". @@ -361,6 +358,14 @@ mobile devices. [CHAR LIMIT=25] --> <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration</string> <!-- Title of the settings for keypress sound volume [CHAR LIMIT=35] --> <string name="prefs_keypress_sound_volume_settings">Keypress sound volume</string> + <!-- Title of the settings for key long press delay [CHAR LIMIT=35] --> + <string name="prefs_key_longpress_timeout_settings">Key long press delay</string> + <!-- TODO: Let's finalize title of the settings and remove translatable="false" --> + <!-- Title of the settings for enabling Emoji palette triggered by the Alt key on physical keyboards [CHAR LIMIT=35] --> + <string name="prefs_enable_emoji_alt_physical_key" translatable="false">Emoji for physical keyboard</string> + <!-- TODO: Let's finalize title of the settings and remove translatable="false" --> + <!-- Description of the settings for enabling Emoji palette triggered by the Alt key on physical keyboards [CHAR LIMIT=64] --> + <string name="prefs_enable_emoji_alt_physical_key_summary" translatable="false">Physical Alt key shows the emoji palette</string> <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] --> <string name="button_default">Default</string> diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml index f7cb10f93..2f768de71 100644 --- a/java/res/values/themes-common.xml +++ b/java/res/values/themes-common.xml @@ -105,8 +105,10 @@ <style name="MainKeyboardView" parent="KeyboardView" /> - <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, - for instance delete button, need themed {@link KeyboardView} attributes. --> + <!-- Though {@link com.android.inputmethod.keyboard.emoji.EmojiPalettesView} doesn't extend + {@link com.android.inputmethod.keyboard.KeyboardView}, some views inside it, for instance + delete button, need themed {@link com.android.inputmethod.keyboard.KeyboardView} + attributes. --> <style name="EmojiPalettesView" /> <style name="MoreKeysKeyboard" /> <style name="MoreKeysKeyboardView" /> diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index ecf40e4e6..26ebd2f03 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -73,8 +73,10 @@ <item name="languageOnSpacebarTextShadowRadius">1.0</item> <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> </style> - <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, - for instance delete button, need themed {@link KeyboardView} attributes. --> + <!-- Though {@link com.android.inputmethod.keyboard.emoji.EmojiPalettesView} doesn't extend + {@link com.android.inputmethod.keyboard.KeyboardView}, some views inside it, for instance + delete button, need themed {@link com.android.inputmethod.keyboard.KeyboardView} + attributes. --> <style name="EmojiPalettesView.ICS" parent="MainKeyboardView.ICS" diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml index de1cd9bf1..c8e11a97f 100644 --- a/java/res/values/themes-klp.xml +++ b/java/res/values/themes-klp.xml @@ -73,8 +73,10 @@ <item name="languageOnSpacebarTextShadowRadius">1.0</item> <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> </style> - <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, - for instance delete button, need themed {@link KeyboardView} attributes. --> + <!-- Though {@link com.android.inputmethod.keyboard.emoji.EmojiPalettesView} doesn't extend + {@link com.android.inputmethod.keyboard.KeyboardView}, some views inside it, for instance + delete button, need themed {@link com.android.inputmethod.keyboard.KeyboardView} + attributes. --> <style name="EmojiPalettesView.KLP" parent="MainKeyboardView.KLP" diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml index b081772e9..0535064d3 100644 --- a/java/res/values/themes-lxx-dark.xml +++ b/java/res/values/themes-lxx-dark.xml @@ -73,8 +73,10 @@ <!-- A negative value to disable text shadow layer. --> <item name="languageOnSpacebarTextShadowRadius">-1.0</item> </style> - <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, - for instance delete button, need themed {@link KeyboardView} attributes. --> + <!-- Though {@link com.android.inputmethod.keyboard.emoji.EmojiPalettesView} doesn't extend + {@link com.android.inputmethod.keyboard.KeyboardView}, some views inside it, for instance + delete button, need themed {@link com.android.inputmethod.keyboard.KeyboardView} + attributes. --> <style name="EmojiPalettesView.LXX_Dark" parent="MainKeyboardView.LXX_Dark" diff --git a/java/res/values/themes-lxx-light.xml b/java/res/values/themes-lxx-light.xml index 3d294e450..80e88b8f1 100644 --- a/java/res/values/themes-lxx-light.xml +++ b/java/res/values/themes-lxx-light.xml @@ -73,8 +73,10 @@ <!-- A negative value to disable text shadow layer. --> <item name="languageOnSpacebarTextShadowRadius">-1.0</item> </style> - <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, - for instance delete button, need themed {@link KeyboardView} attributes. --> + <!-- Though {@link com.android.inputmethod.keyboard.emoji.EmojiPalettesView} doesn't extend + {@link com.android.inputmethod.keyboard.KeyboardView}, some views inside it, for instance + delete button, need themed {@link com.android.inputmethod.keyboard.KeyboardView} + attributes. --> <style name="EmojiPalettesView.LXX_Light" parent="MainKeyboardView.LXX_Light" diff --git a/java/res/xml-sw600dp/key_space_3kw.xml b/java/res/xml-sw600dp/key_space_3kw.xml index 9932d342e..8cc3a38a5 100644 --- a/java/res/xml-sw600dp/key_space_3kw.xml +++ b/java/res/xml-sw600dp/key_space_3kw.xml @@ -22,12 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <switch> - <!-- fa: Perisan - kn: Kannada - ne: Nepali - te: Telugu --> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="true" > <Key @@ -39,7 +35,7 @@ latin:keyStyle="zwnjKeyStyle" /> </case> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="false" > <Key diff --git a/java/res/xml-sw600dp/key_space_7kw.xml b/java/res/xml-sw600dp/key_space_7kw.xml index 3311f812a..61e076534 100644 --- a/java/res/xml-sw600dp/key_space_7kw.xml +++ b/java/res/xml-sw600dp/key_space_7kw.xml @@ -22,12 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <switch> - <!-- fa: Perisan - kn: Kannada - ne: Nepali - te: Telugu --> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="true" > <Key @@ -39,7 +35,7 @@ latin:keyStyle="zwnjKeyStyle" /> </case> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="false" > <Key diff --git a/java/res/xml-sw600dp/rows_number_normal.xml b/java/res/xml-sw600dp/rows_number_normal.xml index 7a4700d5a..e6fdf73ec 100644 --- a/java/res/xml-sw600dp/rows_number_normal.xml +++ b/java/res/xml-sw600dp/rows_number_normal.xml @@ -24,17 +24,17 @@ <Row> <Key latin:keySpec="-" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="+" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="." - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -53,13 +53,15 @@ latin:keyWidth="fillRight" /> </Row> <Row> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="/" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <switch> @@ -70,14 +72,14 @@ latin:keySpec="," latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/morekeys_am_pm" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </case> <default> <Key latin:keySpec="," - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </default> @@ -100,12 +102,12 @@ <Row> <Key latin:keySpec="(" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec=")" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <switch> @@ -114,14 +116,14 @@ > <Key latin:keySpec=":" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </case> <default> <Key latin:keySpec="=" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </default> @@ -143,8 +145,10 @@ <Key latin:keyStyle="tabletNumSpaceKeyStyle" latin:keyWidth="30%p" /> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" latin:keyXPos="31%p" /> <Key latin:keySpec="0" diff --git a/java/res/xml-sw600dp/rows_number_password.xml b/java/res/xml-sw600dp/rows_number_password.xml index 6c3855a01..37e63383e 100644 --- a/java/res/xml-sw600dp/rows_number_password.xml +++ b/java/res/xml-sw600dp/rows_number_password.xml @@ -70,7 +70,8 @@ <Key latin:keyStyle="deleteKeyStyle" /> <Key - latin:keyStyle="num0KeyStyle" /> + latin:keyStyle="num0KeyStyle" + latin:keyHintLabel="+" /> <Key latin:keyStyle="enterKeyStyle" /> <!-- Note: This Spacer prevents the above key from being marked as a right edge key. --> diff --git a/java/res/xml-sw600dp/rows_phone.xml b/java/res/xml-sw600dp/rows_phone.xml index 612397a90..fc86a7670 100644 --- a/java/res/xml-sw600dp/rows_phone.xml +++ b/java/res/xml-sw600dp/rows_phone.xml @@ -28,16 +28,18 @@ <Row> <Key latin:keySpec="-" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="+" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key - latin:keyStyle="numPauseKeyStyle" + latin:keySpec="!string/label_pause_key|," + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -55,16 +57,18 @@ <Row> <Key latin:keySpec="," - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="." - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key - latin:keyStyle="numWaitKeyStyle" + latin:keySpec="!string/label_wait_key|;" + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -82,17 +86,17 @@ <Row> <Key latin:keySpec="(" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec=")" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="N" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -109,13 +113,16 @@ <Key latin:keyStyle="tabletNumSpaceKeyStyle" latin:keyWidth="30%p" /> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" latin:keyXPos="31%p" /> <Key - latin:keyStyle="num0KeyStyle" /> + latin:keyStyle="num0KeyStyle" + latin:keyHintLabel="+" /> <Key latin:keySpec="\#" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> </Row> </merge> diff --git a/java/res/xml/kbd_emoji_category1.xml b/java/res/xml/kbd_emoji_category1.xml index 5145ea9d3..2770cfb15 100644 --- a/java/res/xml/kbd_emoji_category1.xml +++ b/java/res/xml/kbd_emoji_category1.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_faces" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category2.xml b/java/res/xml/kbd_emoji_category2.xml index ac8784f4b..d547056e1 100644 --- a/java/res/xml/kbd_emoji_category2.xml +++ b/java/res/xml/kbd_emoji_category2.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_objects" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category3.xml b/java/res/xml/kbd_emoji_category3.xml index 88c4db92b..2172d9880 100644 --- a/java/res/xml/kbd_emoji_category3.xml +++ b/java/res/xml/kbd_emoji_category3.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_nature" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category4.xml b/java/res/xml/kbd_emoji_category4.xml index 262384d80..46b6d46e8 100644 --- a/java/res/xml/kbd_emoji_category4.xml +++ b/java/res/xml/kbd_emoji_category4.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_places" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category5.xml b/java/res/xml/kbd_emoji_category5.xml index bf823f978..4304701d4 100644 --- a/java/res/xml/kbd_emoji_category5.xml +++ b/java/res/xml/kbd_emoji_category5.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_symbols" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category6.xml b/java/res/xml/kbd_emoji_category6.xml index edb82fc64..516ed7a42 100644 --- a/java/res/xml/kbd_emoji_category6.xml +++ b/java/res/xml/kbd_emoji_category6.xml @@ -28,5 +28,5 @@ <GridRows latin:textsArray="@array/emoji_emoticons" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_recents.xml b/java/res/xml/kbd_emoji_recents.xml index edf3872c1..4953c1079 100644 --- a/java/res/xml/kbd_emoji_recents.xml +++ b/java/res/xml/kbd_emoji_recents.xml @@ -28,5 +28,5 @@ <GridRows latin:codesArray="@array/emoji_recents" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/key_space_5kw.xml b/java/res/xml/key_space_5kw.xml index b1fe0bbeb..692c245ff 100644 --- a/java/res/xml/key_space_5kw.xml +++ b/java/res/xml/key_space_5kw.xml @@ -22,12 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <switch> - <!-- fa: Perisan - kn: Kannada - ne: Nepali - te: Telugu --> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="true" > <Key @@ -39,7 +35,7 @@ latin:keyStyle="zwnjKeyStyle" /> </case> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="false" > <Key diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml index 847b43610..911c2763d 100644 --- a/java/res/xml/key_styles_number.xml +++ b/java/res/xml/key_styles_number.xml @@ -33,9 +33,7 @@ latin:keyLabelFlags="fontNormal|followKeyLetterRatio|followFunctionalTextColor" latin:parentStyle="numKeyBaseStyle" /> <key-style - latin:styleName="numFunctionalKeyStyle" - latin:keyLabelFlags="followKeyLargeLetterRatio" - latin:backgroundType="functional" + latin:styleName="numSymbolKeyStyle" latin:parentStyle="numKeyBaseStyle" /> <key-style latin:styleName="numberKeyStyle" @@ -44,7 +42,6 @@ <key-style latin:styleName="num0KeyStyle" latin:keySpec="0" - latin:keyHintLabel="+" latin:parentStyle="numberKeyStyle" /> <key-style latin:styleName="num1KeyStyle" @@ -90,11 +87,6 @@ latin:keySpec="9" latin:keyHintLabel="WXYZ" latin:parentStyle="numberKeyStyle" /> - <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> - <key-style - latin:styleName="numStarKeyStyle" - latin:keySpec="*|*" - latin:parentStyle="numKeyStyle" /> <!-- Only for non-tablet device --> <key-style latin:styleName="numPhoneToSymbolKeyStyle" @@ -105,16 +97,6 @@ latin:keySpec="!text/keylabel_to_phone_numeric|!code/key_switch_alpha_symbol" latin:parentStyle="numModeKeyStyle" /> <key-style - latin:styleName="numPauseKeyStyle" - latin:keySpec="!text/label_pause_key|," - latin:keyLabelFlags="followKeyHintLabelRatio|autoXScale" - latin:parentStyle="numKeyBaseStyle" /> - <key-style - latin:styleName="numWaitKeyStyle" - latin:keySpec="!text/label_wait_key|;" - latin:keyLabelFlags="followKeyHintLabelRatio|autoXScale" - latin:parentStyle="numKeyBaseStyle" /> - <key-style latin:styleName="numTabKeyStyle" latin:keyActionFlags="noKeyPreview" latin:parentStyle="tabKeyStyle" /> diff --git a/java/res/xml/keyboard_layout_set_bengali_akkhor.xml b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml index b2b09b22d..267064d46 100644 --- a/java/res/xml/keyboard_layout_set_bengali_akkhor.xml +++ b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml @@ -19,7 +19,7 @@ --> <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Feature latin:supportedScript="devanagari" /> + <Feature latin:supportedScript="bengali" /> <Element latin:elementKeyboard="@xml/kbd_bengali_akkhor" latin:elementName="alphabet" diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 77a46f9c7..5f05e8b18 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -28,7 +28,7 @@ be_BY: Belarusian (Belarus)/east_slavic bg: Bulgarian/bulgarian bg: Bulgarian/bulgarian_bds - (bn_BD: Bengali (Bangladesh)/bengali_akkhor) # This is a preliminary keyboard layout. + bn_BD: Bengali (Bangladesh)/bengali_akkhor # This is a preliminary keyboard layout. bn_IN: Bengali (India)/bengali ca: Catalan/spanish cs: Czech/qwertz @@ -53,7 +53,7 @@ gl_ES: Galician (Spain)/spanish hi: Hindi/hindi hi: Hindi/hindi_compact - (hi_ZZ: Hinglish/qwerty) # This is a preliminary keyboard layout. + hi_ZZ: Hinglish/qwerty # This is a preliminary keyboard layout. hr: Croatian/qwertz hu: Hungarian/qwertz hy_AM: Armenian (Armenia) Phonetic/armenian_phonetic @@ -75,7 +75,7 @@ mn_MN: Mongolian (Mongolia)/mongolian mr_IN: Marathi (India)/marathi ms_MY: Malay (Malaysia)/qwerty - (my_MM: Myanmar (Myanmar)/myanmar) # This is a preliminary keyboard layout. + my_MM: Myanmar (Myanmar)/myanmar # This is a preliminary keyboard layout. nb: Norwegian Bokmål/nordic ne_NP: Nepali (Nepal) Romanized/nepali_romanized ne_NP: Nepali (Nepal) Traditional/nepali_traditional @@ -86,22 +86,22 @@ pt_PT: Portuguese (Portugal)/qwerty ro: Romanian/qwerty ru: Russian/east_slavic - (si_LK: Sinhala (Sri Lanka)/sinhala) # This is a preliminary keyboard layout. + si_LK: Sinhala (Sri Lanka)/sinhala # This is a preliminary keyboard layout. sk: Slovak/qwerty sl: Slovenian/qwerty sr: Serbian/south_slavic - (sr_ZZ: Serbian (Latin)/serbian_qwertz) # This is a preliminary keyboard layout. + sr_ZZ: Serbian (Latin)/serbian_qwertz # This is a preliminary keyboard layout. sv: Swedish/nordic sw: Swahili/qwerty ta_IN: Tamil (India)/tamil - (ta_LK: Tamil (Sri Lanka)/tamil) # Disabled in conjunction with si_LK. + ta_LK: Tamil (Sri Lanka)/tamil # Disabled in conjunction with si_LK. ta_SG: Tamil (Singapore)/tamil te_IN: Telugu (India)/telugu th: Thai/thai tl: Tagalog/spanish tr: Turkish/qwerty uk: Ukrainian/east_slavic - (uz_UZ: Uzbek (Uzbekistan)/uzbek) # This is a preliminary keyboard layout. + uz_UZ: Uzbek (Uzbekistan)/uzbek # This is a preliminary keyboard layout. vi: Vietnamese/qwerty zu: Zulu/qwerty zz: QWERTY/qwerty @@ -183,7 +183,6 @@ /> <!-- TODO: This Bengali (Bangladesh) keyboard is a preliminary layout. This isn't based on the final specification. --> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xa2144b0c" @@ -192,7 +191,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=bengali_akkhor,EmojiCapable" android:isAsciiCapable="false" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xbff5986c" @@ -371,7 +369,6 @@ /> <!-- TODO: This Hinglish keyboard is a preliminary layout. This isn't based on the final specification. --> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_hi_ZZ" android:subtypeId="0x352eb37c" @@ -380,7 +377,6 @@ android:imeSubtypeExtraValue="AsciiCapable,KeyboardLayoutSet=qwerty,EmojiCapable" android:isAsciiCapable="true" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x35b7526a" @@ -553,7 +549,6 @@ /> <!-- TODO: This Myanmar keyboard is a preliminary layout. This isn't based on the final specification. --> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xea266ea4" @@ -562,7 +557,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=myanmar,EmojiCapable,CombiningRules=MyanmarReordering" android:isAsciiCapable="false" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x3f12ee14" @@ -645,7 +639,6 @@ /> <!-- TODO: This Sinhala keyboard is a preliminary layout. This isn't based on the final specification. --> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x5c6b3bde" @@ -654,7 +647,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=sinhala,EmojiCapable" android:isAsciiCapable="false" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8e94d413" @@ -681,7 +673,6 @@ /> <!-- TODO: This Serbian Latin keyboard is a preliminary layout. This isn't based on the final specification. --> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_sr_ZZ" android:subtypeId="0xf4a5569c" @@ -690,7 +681,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=serbian_qwertz,AsciiCapable,EmojiCapable" android:isAsciiCapable="true" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x48b4ff43" @@ -716,7 +706,6 @@ android:isAsciiCapable="false" /> <!-- TODO: Enabling/Disabling ta_LK subtype must be aligned with si_LK subtype. --> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x6ca12d84" @@ -725,7 +714,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable" android:isAsciiCapable="false" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x785abbd9" @@ -776,7 +764,6 @@ /> <!-- TODO: This Uzbek keyboard is a preliminary layout. This isn't based on the final specification. --> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xad5cf7f6" @@ -785,7 +772,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=uzbek,AsciiCapable,EmojiCapable" android:isAsciiCapable="true" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x93972eee" diff --git a/java/res/xml/prefs_screen_advanced.xml b/java/res/xml/prefs_screen_advanced.xml index 5aefcc8d5..1fa6fd0c4 100644 --- a/java/res/xml/prefs_screen_advanced.xml +++ b/java/res/xml/prefs_screen_advanced.xml @@ -31,6 +31,18 @@ android:key="pref_keypress_sound_volume" android:title="@string/prefs_keypress_sound_volume_settings" latin:maxValue="100" /> <!-- percent --> + <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:key="pref_key_longpress_timeout" + android:title="@string/prefs_key_longpress_timeout_settings" + latin:minValue="@integer/config_min_longpress_timeout" + latin:maxValue="@integer/config_max_longpress_timeout" + latin:stepValue="@integer/config_longpress_timeout_step" /> + <CheckBoxPreference + android:key="pref_enable_emoji_alt_physical_key" + android:title="@string/prefs_enable_emoji_alt_physical_key" + android:summary="@string/prefs_enable_emoji_alt_physical_key_summary" + android:defaultValue="true" + android:persistent="true" /> <!-- The settings for showing setup wizard application icon shouldn't be persistent and the default value is added programmatically. --> <CheckBoxPreference diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml index 25f7c6612..905bc045c 100644 --- a/java/res/xml/prefs_screen_debug.xml +++ b/java/res/xml/prefs_screen_debug.xml @@ -31,11 +31,6 @@ android:defaultValue="false" android:persistent="true" /> <CheckBoxPreference - android:key="force_physical_keyboard_special_key" - android:title="@string/prefs_force_physical_keyboard_special_key" - android:defaultValue="false" - android:persistent="true" /> - <CheckBoxPreference android:key="pref_should_show_lxx_suggestion_ui" android:title="@string/prefs_should_show_lxx_suggestion_ui" android:defaultValue="true" @@ -46,12 +41,6 @@ android:summary="@string/sliding_key_input_preview_summary" android:defaultValue="true" android:persistent="true" /> - <com.android.inputmethod.latin.settings.SeekBarDialogPreference - android:key="pref_key_longpress_timeout" - android:title="@string/prefs_key_longpress_timeout_settings" - latin:minValue="@integer/config_min_longpress_timeout" - latin:maxValue="@integer/config_max_longpress_timeout" - latin:stepValue="@integer/config_longpress_timeout_step" /> <CheckBoxPreference android:key="pref_has_custom_key_preview_animation_params" android:title="@string/prefs_customize_key_preview_animation" @@ -87,6 +76,17 @@ android:key="pref_key_preview_dismiss_duration" android:title="@string/prefs_key_popup_dismiss_duration_settings" latin:maxValue="100" /> <!-- milliseconds --> + <CheckBoxPreference + android:key="pref_resize_keyboard" + android:title="@string/prefs_resize_keyboard" + android:defaultValue="false" + android:persistent="true" /> + <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:dependency="pref_resize_keyboard" + android:key="pref_keyboard_height_scale" + android:title="@string/prefs_keyboard_height_scale" + latin:minValue="50" + latin:maxValue="120" /> <!-- percentage --> <PreferenceScreen android:key="read_external_dictionary" android:title="@string/prefs_read_external_dictionary" /> diff --git a/java/res/xml/rowkeys_telugu2.xml b/java/res/xml/rowkeys_telugu2.xml index a472fd3f8..f1ce459e5 100644 --- a/java/res/xml/rowkeys_telugu2.xml +++ b/java/res/xml/rowkeys_telugu2.xml @@ -51,10 +51,10 @@ latin:moreKeys="ఫ" /> <!-- U+0C30: "ర" TELUGU LETTER RA U+0C31: "ఱ" TELUGU LETTER RRA - U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R --> + U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA --> <Key latin:keySpec="ర" - latin:moreKeys="ఱ,ృ" /> + latin:moreKeys="ఱ,్ర" /> <!-- U+0C15: "క" TELUGU LETTER KA U+0C16: "ఖ" TELUGU LETTER KHA --> <Key diff --git a/java/res/xml/rowkeys_telugu3.xml b/java/res/xml/rowkeys_telugu3.xml index 05755ec4d..2e3730ac7 100644 --- a/java/res/xml/rowkeys_telugu3.xml +++ b/java/res/xml/rowkeys_telugu3.xml @@ -19,24 +19,28 @@ --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> - <!-- U+0C46: "ె" TELUGU VOWEL SIGN E + <!-- U+0C4A: "ొ" TELUGU VOWEL SIGN O U+0C12: "ఒ" TELUGU LETTER O --> <Key - latin:keySpec="ె" + latin:keySpec="ొ" latin:moreKeys="ఒ" /> - <!-- U+0C02: "ం" TELUGU SIGN ANUSVARA + <!-- U+0C46: "ె" TELUGU VOWEL SIGN E U+0C0E: "ఎ" TELUGU LETTER E --> <Key - latin:keySpec="ం" + latin:keySpec="ె" latin:moreKeys="ఎ" /> - <!-- U+0C2E: "మ" TELUGU LETTER MA --> - <Key latin:keySpec="మ" /> + <!-- U+0C2E: "మ" TELUGU LETTER MA + U+0C02: "ం" TELUGU SIGN ANUSVARA + U+0C01: "ఁ" TELUGU SIGN CANDRABINDU --> + <Key latin:keySpec="మ" + latin:moreKeys="ం,ఁ" /> <!-- U+0C28: "న" TELUGU LETTER NA U+0C23: "ణ" TELUGU LETTER NNA - U+0C19: "ఙ" TELUGU LETTER NGA --> + U+0C19: "ఙ" TELUGU LETTER NGA + U+0C1E: "ఞ" TELUGU LETTER NYA --> <Key latin:keySpec="న" - latin:moreKeys="ణ,ఙ" /> + latin:moreKeys="ణ,ఙ,ఞ" /> <!-- U+0C35: "వ" TELUGU LETTER VA --> <Key latin:keySpec="వ" /> <!-- U+0C32: "ల" TELUGU LETTER LA @@ -50,10 +54,10 @@ latin:keySpec="స" latin:moreKeys="శ" /> <!-- U+0C0B: "ఋ" TELUGU LETTER VOCALIC R - U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA --> + U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R --> <Key latin:keySpec="ఋ" - latin:moreKeys="్ర" /> + latin:moreKeys="ృ" /> <!-- U+0C37: "ష" TELUGU LETTER SSA U+0C15/U+0C4D/U+0C37: "క్ష" TELUGU LETTER KA/TELUGU SIGN VIRAMA/TELUGU LETTER SSA --> <Key diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml index d8d15080e..0f92ac605 100644 --- a/java/res/xml/rows_number_normal.xml +++ b/java/res/xml/rows_number_normal.xml @@ -35,7 +35,8 @@ latin:keySpec="-" latin:moreKeys="+" latin:keyLabelFlags="hasPopupHint" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> @@ -54,7 +55,8 @@ > <Key latin:keySpec="." - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </case> <case @@ -62,15 +64,17 @@ > <Key latin:keySpec="." - latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/morekeys_am_pm" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyLabelFlags="hasPopupHint" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </case> <default> <Key latin:keySpec="," - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </default> </switch> diff --git a/java/res/xml/rows_number_password.xml b/java/res/xml/rows_number_password.xml index 2e61a08ae..65736c430 100644 --- a/java/res/xml/rows_number_password.xml +++ b/java/res/xml/rows_number_password.xml @@ -70,7 +70,8 @@ <Key latin:keyStyle="deleteKeyStyle" /> <Key - latin:keyStyle="num0KeyStyle" /> + latin:keyStyle="num0KeyStyle" + latin:keyHintLabel="+" /> <Key latin:keyStyle="enterKeyStyle" /> <!-- Note: This Spacer prevents the above key from being marked as a right edge key. --> diff --git a/java/res/xml/rows_phone.xml b/java/res/xml/rows_phone.xml index 03e45419a..bb5590d4e 100644 --- a/java/res/xml/rows_phone.xml +++ b/java/res/xml/rows_phone.xml @@ -36,7 +36,8 @@ latin:keySpec="-" latin:moreKeys="+" latin:keyLabelFlags="hasPopupHint" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> @@ -48,7 +49,8 @@ latin:keyStyle="num6KeyStyle" /> <Key latin:keySpec="." - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> diff --git a/java/res/xml/rows_phone_symbols.xml b/java/res/xml/rows_phone_symbols.xml index 983bfb5c8..195a183a3 100644 --- a/java/res/xml/rows_phone_symbols.xml +++ b/java/res/xml/rows_phone_symbols.xml @@ -28,45 +28,53 @@ <Row> <Key latin:keySpec="(" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="/" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec=")" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="-" latin:moreKeys="+" latin:keyLabelFlags="hasPopupHint" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> <Key latin:keySpec="N" - latin:keyStyle="numKeyBaseStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <!-- Pause is a comma. Check PhoneNumberUtils.java to see if this has changed. --> <Key - latin:keyStyle="numPauseKeyStyle" /> + latin:keySpec="!string/label_pause_key|," + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="," - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="." - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" /> + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" /> <!-- Wait is a semicolon. --> <Key - latin:keyStyle="numWaitKeyStyle" /> + latin:keySpec="!string/label_wait_key|;" + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="\#" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keyStyle="deleteKeyStyle" latin:keyWidth="fillRight" /> @@ -76,7 +84,7 @@ latin:keyStyle="numPhoneToNumericKeyStyle" /> <Key latin:keySpec="+" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keyStyle="numSpaceKeyStyle" /> <Key diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index edcdd4c4c..bbda9f8e2 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -26,9 +26,9 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Locale; @@ -346,7 +346,8 @@ final class KeyCodeDescriptionMapper { } // TODO: Remove this method once TTS supports emoticon verbalization. - private String getSpokenEmoticonDescription(final Context context, final String outputText) { + private static String getSpokenEmoticonDescription(final Context context, + final String outputText) { final StringBuilder sb = new StringBuilder(SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX); final int textLength = outputText.length(); for (int index = 0; index < textLength; index = outputText.offsetByCodePoints(index, 1)) { diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java index 66b0acb2f..7fc1e9d8a 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java +++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java @@ -329,9 +329,8 @@ final class KeyboardAccessibilityNodeProvider<KV extends KeyboardView> if (currentSettings.isWordSeparator(key.getCode())) { return mAccessibilityUtils.getAutoCorrectionDescription( keyCodeDescription, shouldObscure); - } else { - return keyCodeDescription; } + return keyCodeDescription; } /** diff --git a/java/src/com/android/inputmethod/compat/BuildCompatUtils.java b/java/src/com/android/inputmethod/compat/BuildCompatUtils.java index 7d1717bd1..5d56f12ae 100644 --- a/java/src/com/android/inputmethod/compat/BuildCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/BuildCompatUtils.java @@ -33,11 +33,4 @@ public final class BuildCompatUtils { public static final int EFFECTIVE_SDK_INT = IS_RELEASE_BUILD ? Build.VERSION.SDK_INT : Build.VERSION.SDK_INT + 1; - - /** - * API version for L-release. - */ - // TODO: Substitute this constant reference with Build.VERSION_CODES.L* once the *next* version - // becomes available. - public static final int VERSION_CODES_LXX = 21; } diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java index 6aa2736c1..5db80190c 100644 --- a/java/src/com/android/inputmethod/compat/CompatUtils.java +++ b/java/src/com/android/inputmethod/compat/CompatUtils.java @@ -144,7 +144,7 @@ public final class CompatUtils { public <T> ToObjectMethodWrapper<T> getMethod(final String name, final T defaultValue, final Class<?>... parameterTypes) { - return new ToObjectMethodWrapper<T>(CompatUtils.getMethod(mClass, name, parameterTypes), + return new ToObjectMethodWrapper<>(CompatUtils.getMethod(mClass, name, parameterTypes), defaultValue); } diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java index f4f54b624..01a9e6712 100644 --- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java @@ -46,14 +46,14 @@ public class CursorAnchorInfoCompatWrapper { */ public static final int FLAG_IS_RTL = 0x04; - private CursorAnchorInfoCompatWrapper() { + CursorAnchorInfoCompatWrapper() { // This class is not publicly instantiable. } - @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Nullable public static CursorAnchorInfoCompatWrapper wrap(@Nullable final CursorAnchorInfo instance) { - if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return null; } if (instance == null) { @@ -82,10 +82,12 @@ public class CursorAnchorInfoCompatWrapper { throw new UnsupportedOperationException("not supported."); } + @SuppressWarnings("unused") public RectF getCharacterBounds(final int index) { throw new UnsupportedOperationException("not supported."); } + @SuppressWarnings("unused") public int getCharacterBoundsFlags(final int index) { throw new UnsupportedOperationException("not supported."); } @@ -110,7 +112,7 @@ public class CursorAnchorInfoCompatWrapper { throw new UnsupportedOperationException("not supported."); } - @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static final class RealWrapper extends CursorAnchorInfoCompatWrapper { @Nonnull diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java index b9a536721..3a27c5739 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java @@ -20,8 +20,8 @@ import android.os.Build; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.common.Constants; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -52,6 +52,7 @@ public final class InputMethodSubtypeCompatUtils { // This utility class is not publicly instantiable. } + @SuppressWarnings("deprecation") public static InputMethodSubtype newInputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) { diff --git a/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java b/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java index f411f181b..58e5a36b6 100644 --- a/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/LocaleSpanCompatUtils.java @@ -17,6 +17,7 @@ package com.android.inputmethod.compat; import android.text.Spannable; +import android.text.Spanned; import android.text.style.LocaleSpan; import android.util.Log; @@ -127,13 +128,13 @@ public final class LocaleSpanCompatUtils { final int spanFlag = spannable.getSpanFlags(existingLocaleSpan); if (spanStart < newStart) { newStart = spanStart; - isStartExclusive = ((spanFlag & Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) == - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + isStartExclusive = ((spanFlag & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } if (newEnd < spanEnd) { newEnd = spanEnd; - isEndExclusive = ((spanFlag & Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) == - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + isEndExclusive = ((spanFlag & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } existingLocaleSpansToBeMerged.add(existingLocaleSpan); } @@ -201,24 +202,17 @@ public final class LocaleSpanCompatUtils { private static int getSpanFlag(final int originalFlag, final boolean isStartExclusive, final boolean isEndExclusive) { - return (originalFlag & ~Spannable.SPAN_POINT_MARK_MASK) | + return (originalFlag & ~Spanned.SPAN_POINT_MARK_MASK) | getSpanPointMarkFlag(isStartExclusive, isEndExclusive); } private static int getSpanPointMarkFlag(final boolean isStartExclusive, final boolean isEndExclusive) { if (isStartExclusive) { - if (isEndExclusive) { - return Spannable.SPAN_EXCLUSIVE_EXCLUSIVE; - } else { - return Spannable.SPAN_EXCLUSIVE_INCLUSIVE; - } - } else { - if (isEndExclusive) { - return Spannable.SPAN_INCLUSIVE_EXCLUSIVE; - } else { - return Spannable.SPAN_INCLUSIVE_INCLUSIVE; - } + return isEndExclusive ? Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + : Spanned.SPAN_EXCLUSIVE_INCLUSIVE; } + return isEndExclusive ? Spanned.SPAN_INCLUSIVE_EXCLUSIVE + : Spanned.SPAN_INCLUSIVE_INCLUSIVE; } } diff --git a/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java index eb180071e..70ab972c5 100644 --- a/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java @@ -71,13 +71,13 @@ public class NotificationCompatUtils { CompatUtils.invoke(builder, null, METHOD_setPriority, PRIORITY_LOW); } + @SuppressWarnings("deprecation") public static Notification build(final Notification.Builder builder) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // #build was added in API level 16, JELLY_BEAN return (Notification) CompatUtils.invoke(builder, null, METHOD_build); - } else { - // #getNotification was deprecated in API level 16, JELLY_BEAN - return builder.getNotification(); } + // #getNotification was deprecated in API level 16, JELLY_BEAN + return builder.getNotification(); } } diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java index 1fb597ba6..b78c357ab 100644 --- a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java @@ -16,42 +16,33 @@ package com.android.inputmethod.compat; +import android.annotation.TargetApi; import android.content.Context; -import android.provider.UserDictionary.Words; +import android.os.Build; +import android.provider.UserDictionary; -import java.lang.reflect.Method; import java.util.Locale; public final class UserDictionaryCompatUtils { - // UserDictionary.Words#addWord(Context, String, int, String, Locale) was introduced - // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). - private static final Method METHOD_addWord = CompatUtils.getMethod(Words.class, "addWord", - Context.class, String.class, int.class, String.class, Locale.class); - @SuppressWarnings("deprecation") public static void addWord(final Context context, final String word, final int freq, final String shortcut, final Locale locale) { - if (hasNewerAddWord()) { - CompatUtils.invoke(Words.class, null, METHOD_addWord, context, word, freq, shortcut, - locale); - } else { - // Fall back to the pre-JellyBean method. - final int localeType; - if (null == locale) { - localeType = Words.LOCALE_TYPE_ALL; - } else { - final Locale currentLocale = context.getResources().getConfiguration().locale; - if (locale.equals(currentLocale)) { - localeType = Words.LOCALE_TYPE_CURRENT; - } else { - localeType = Words.LOCALE_TYPE_ALL; - } - } - Words.addWord(context, word, freq, localeType); + if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + addWordWithShortcut(context, word, freq, shortcut, locale); + return; } + // Fall back to the pre-JellyBean method. + final Locale currentLocale = context.getResources().getConfiguration().locale; + final int localeType = currentLocale.equals(locale) + ? UserDictionary.Words.LOCALE_TYPE_CURRENT : UserDictionary.Words.LOCALE_TYPE_ALL; + UserDictionary.Words.addWord(context, word, freq, localeType); } - public static final boolean hasNewerAddWord() { - return null != METHOD_addWord; + // {@link UserDictionary.Words#addWord(Context,String,int,String,Locale)} was introduced + // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private static void addWordWithShortcut(final Context context, final String word, + final int freq, final String shortcut, final Locale locale) { + UserDictionary.Words.addWord(context, word, freq, shortcut, locale); } } diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java index 52b8b74e8..0c8e5b77d 100644 --- a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java @@ -17,6 +17,7 @@ package com.android.inputmethod.compat; import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.view.View; public class ViewOutlineProviderCompatUtils { @@ -34,7 +35,7 @@ public class ViewOutlineProviderCompatUtils { }; public static InsetsUpdater setInsetsOutlineProvider(final View view) { - if (BuildCompatUtils.EFFECTIVE_SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return EMPTY_INSETS_UPDATER; } return ViewOutlineProviderCompatUtilsLXX.setInsetsOutlineProvider(view); diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java index f9917ac11..5bbb5ce99 100644 --- a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java +++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java @@ -25,7 +25,7 @@ import android.view.ViewOutlineProvider; import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; -@TargetApi(Build.VERSION_CODES.L) +@TargetApi(Build.VERSION_CODES.LOLLIPOP) class ViewOutlineProviderCompatUtilsLXX { private ViewOutlineProviderCompatUtilsLXX() { // This utility class is not publicly instantiable. diff --git a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java index 6d6c8f5c6..0fa72c3fd 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java +++ b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java @@ -122,19 +122,23 @@ public class ButtonSwitcher extends FrameLayout { mDeleteButton.setTranslationX(STATUS_DELETE == status ? 0 : width); } + // The helper method for {@link AnimatorListenerAdapter}. + void animateButtonIfStatusIsEqual(final View newButton, final int newStatus) { + if (newStatus != mStatus) return; + animateButton(newButton, ANIMATION_IN); + } + private void animateButtonPosition(final int oldStatus, final int newStatus) { final View oldButton = getButton(oldStatus); final View newButton = getButton(newStatus); if (null != oldButton && null != newButton) { // Transition between two buttons : animate out, then in - animateButton(oldButton, ANIMATION_OUT).setListener( - new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(final Animator animation) { - if (newStatus != mStatus) return; - animateButton(newButton, ANIMATION_IN); - } - }); + animateButton(oldButton, ANIMATION_OUT).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(final Animator animation) { + animateButtonIfStatusIsEqual(newButton, newStatus); + } + }); } else if (null != oldButton) { animateButton(oldButton, ANIMATION_OUT); } else if (null != newButton) { @@ -159,9 +163,8 @@ public class ButtonSwitcher extends FrameLayout { if (ANIMATION_IN == direction) { button.setClickable(true); return button.animate().translationX(0); - } else { - button.setClickable(false); - return button.animate().translationX(outerX - innerX); } + button.setClickable(false); + return button.animate().translationX(outerX - innerX); } } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java index 1d84e5888..759852025 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java @@ -148,7 +148,7 @@ public class DictionaryDownloadProgressBar extends ProgressBar { } } - private class UpdateHelper implements Runnable { + class UpdateHelper implements Runnable { private int mProgress; @Override public void run() { diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java index 8e026171d..836340a75 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java @@ -32,7 +32,7 @@ import java.util.HashMap; * in case some dictionaries appeared, disappeared, changed states etc. */ public class DictionaryListInterfaceState { - private static class State { + static class State { public boolean mOpen = false; public int mStatus = MetadataDbHelper.STATUS_UNKNOWN; } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java index e748321e2..37fa76be7 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java @@ -255,10 +255,9 @@ public final class DictionaryProvider extends ContentProvider { if (null != dictFiles && dictFiles.size() > 0) { PrivateLog.log("Returned " + dictFiles.size() + " files"); return new ResourcePathCursor(dictFiles); - } else { - PrivateLog.log("No dictionary files for this URL"); - return new ResourcePathCursor(Collections.<WordListInfo>emptyList()); } + PrivateLog.log("No dictionary files for this URL"); + return new ResourcePathCursor(Collections.<WordListInfo>emptyList()); // V2_METADATA and V2_DATAFILE are not supported for query() default: return null; @@ -319,14 +318,13 @@ public final class DictionaryProvider extends ContentProvider { final AssetFileDescriptor afd = getContext().getResources().openRawResourceFd( R.raw.empty); return afd; - } else { - final String localFilename = - wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN); - final File f = getContext().getFileStreamPath(localFilename); - final ParcelFileDescriptor pfd = - ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); - return new AssetFileDescriptor(pfd, 0, pfd.getStatSize()); } + final String localFilename = + wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN); + final File f = getContext().getFileStreamPath(localFilename); + final ParcelFileDescriptor pfd = + ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); + return new AssetFileDescriptor(pfd, 0, pfd.getStatSize()); } catch (FileNotFoundException e) { // No file : fall through and return null } @@ -461,13 +459,16 @@ public final class DictionaryProvider extends ContentProvider { final String wordlistId = uri.getLastPathSegment(); final String clientId = getClientId(uri); final ContentValues wordList = getWordlistMetadataForWordlistId(clientId, wordlistId); - if (null == wordList) return 0; + if (null == wordList) { + return 0; + } final int status = wordList.getAsInteger(MetadataDbHelper.STATUS_COLUMN); final int version = wordList.getAsInteger(MetadataDbHelper.VERSION_COLUMN); if (MetadataDbHelper.STATUS_DELETING == status) { UpdateHandler.markAsDeleted(getContext(), clientId, wordlistId, version, status); return 1; - } else if (MetadataDbHelper.STATUS_INSTALLED == status) { + } + if (MetadataDbHelper.STATUS_INSTALLED == status) { final String result = uri.getQueryParameter(QUERY_PARAMETER_DELETE_RESULT); if (QUERY_PARAMETER_FAILURE.equals(result)) { if (DEBUG) { @@ -480,15 +481,10 @@ public final class DictionaryProvider extends ContentProvider { wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN); final File f = getContext().getFileStreamPath(localFilename); // f.delete() returns true if the file was successfully deleted, false otherwise - if (f.delete()) { - return 1; - } else { - return 0; - } - } else { - Log.e(TAG, "Attempt to delete a file whose status is " + status); - return 0; + return f.delete() ? 1 : 0; } + Log.e(TAG, "Attempt to delete a file whose status is " + status); + return 0; } /** diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java index 568c80abd..e9b634eec 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java @@ -179,7 +179,7 @@ public final class DictionaryService extends Service { return Service.START_REDELIVER_INTENT; } - private static void dispatchBroadcast(final Context context, final Intent intent) { + static void dispatchBroadcast(final Context context, final Intent intent) { if (DATE_CHANGED_INTENT_ACTION.equals(intent.getAction())) { // This happens when the date of the device changes. This normally happens // at midnight local time, but it may happen if the user changes the date diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java index 4366348d5..284032beb 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java @@ -18,7 +18,9 @@ package com.android.inputmethod.dictionarypack; import com.android.inputmethod.latin.utils.FragmentUtils; +import android.annotation.TargetApi; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceActivity; @@ -44,8 +46,8 @@ public final class DictionarySettingsActivity extends PreferenceActivity { return modIntent; } - // TODO: Uncomment the override annotation once we start using SDK version 19. - // @Override + @TargetApi(Build.VERSION_CODES.KITKAT) + @Override public boolean isValidFragment(String fragmentName) { return FragmentUtils.isValidFragment(fragmentName); } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index 11982fa65..c2dc87900 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -31,7 +31,6 @@ import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.text.TextUtils; -import android.text.format.DateUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -203,25 +202,19 @@ public final class DictionarySettingsFragment extends PreferenceFragment @Override public void updateCycleCompleted() {} - private void refreshNetworkState() { + void refreshNetworkState() { NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); boolean isConnected = null == info ? false : info.isConnected(); if (null != mUpdateNowMenu) mUpdateNowMenu.setEnabled(isConnected); } - private void refreshInterface() { + void refreshInterface() { final Activity activity = getActivity(); if (null == activity) return; - final long lastUpdateDate = - MetadataDbHelper.getLastUpdateDateForClient(getActivity(), mClientId); final PreferenceGroup prefScreen = getPreferenceScreen(); final Collection<? extends Preference> prefList = createInstalledDictSettingsCollection(mClientId); - final String updateNowSummary = getString(R.string.last_update) + " " - + DateUtils.formatDateTime(activity, lastUpdateDate, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME); - activity.runOnUiThread(new Runnable() { @Override public void run() { @@ -239,14 +232,14 @@ public final class DictionarySettingsFragment extends PreferenceFragment }); } - private Preference createErrorMessage(final Activity activity, final int messageResource) { + private static Preference createErrorMessage(final Activity activity, final int messageResource) { final Preference message = new Preference(activity); message.setTitle(messageResource); message.setEnabled(false); return message; } - private void removeAnyDictSettings(final PreferenceGroup prefGroup) { + static void removeAnyDictSettings(final PreferenceGroup prefGroup) { for (int i = prefGroup.getPreferenceCount() - 1; i >= 0; --i) { prefGroup.removePreference(prefGroup.getPreference(i)); } @@ -276,7 +269,7 @@ public final class DictionarySettingsFragment extends PreferenceFragment .appendQueryParameter(DictionaryProvider.QUERY_PARAMETER_PROTOCOL_VERSION, "2") .build(); final Activity activity = getActivity(); - final Cursor cursor = null == activity ? null + final Cursor cursor = (null == activity) ? null : activity.getContentResolver().query(contentUri, null, null, null, null); if (null == cursor) { @@ -289,61 +282,57 @@ public final class DictionarySettingsFragment extends PreferenceFragment final ArrayList<Preference> result = new ArrayList<>(); result.add(createErrorMessage(activity, R.string.no_dictionaries_available)); return result; - } else { - final String systemLocaleString = Locale.getDefault().toString(); - final TreeMap<String, WordListPreference> prefMap = new TreeMap<>(); - final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); - final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN); - final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN); - final int descriptionIndex = - cursor.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN); - final int statusIndex = cursor.getColumnIndex(MetadataDbHelper.STATUS_COLUMN); - final int filesizeIndex = cursor.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN); - do { - final String wordlistId = cursor.getString(idIndex); - final int version = cursor.getInt(versionIndex); - final String localeString = cursor.getString(localeIndex); - final Locale locale = new Locale(localeString); - final String description = cursor.getString(descriptionIndex); - final int status = cursor.getInt(statusIndex); - final int matchLevel = - LocaleUtils.getMatchLevel(systemLocaleString, localeString); - final String matchLevelString = - LocaleUtils.getMatchLevelSortedString(matchLevel); - final int filesize = cursor.getInt(filesizeIndex); - // The key is sorted in lexicographic order, according to the match level, then - // the description. - final String key = matchLevelString + "." + description + "." + wordlistId; - final WordListPreference existingPref = prefMap.get(key); - if (null == existingPref || existingPref.hasPriorityOver(status)) { - final WordListPreference oldPreference = mCurrentPreferenceMap.get(key); - final WordListPreference pref; - if (null != oldPreference - && oldPreference.mVersion == version - && oldPreference.hasStatus(status) - && oldPreference.mLocale.equals(locale)) { - // If the old preference has all the new attributes, reuse it. Ideally, - // we should reuse the old pref even if its status is different and call - // setStatus here, but setStatus calls Preference#setSummary() which - // needs to be done on the UI thread and we're not on the UI thread - // here. We could do all this work on the UI thread, but in this case - // it's probably lighter to stay on a background thread and throw this - // old preference out. - pref = oldPreference; - } else { - // Otherwise, discard it and create a new one instead. - // TODO: when the status is different from the old one, we need to - // animate the old one out before animating the new one in. - pref = new WordListPreference(activity, mDictionaryListInterfaceState, - mClientId, wordlistId, version, locale, description, status, - filesize); - } - prefMap.put(key, pref); - } - } while (cursor.moveToNext()); - mCurrentPreferenceMap = prefMap; - return prefMap.values(); } + final String systemLocaleString = Locale.getDefault().toString(); + final TreeMap<String, WordListPreference> prefMap = new TreeMap<>(); + final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); + final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN); + final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN); + final int descriptionIndex = cursor.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN); + final int statusIndex = cursor.getColumnIndex(MetadataDbHelper.STATUS_COLUMN); + final int filesizeIndex = cursor.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN); + do { + final String wordlistId = cursor.getString(idIndex); + final int version = cursor.getInt(versionIndex); + final String localeString = cursor.getString(localeIndex); + final Locale locale = new Locale(localeString); + final String description = cursor.getString(descriptionIndex); + final int status = cursor.getInt(statusIndex); + final int matchLevel = LocaleUtils.getMatchLevel(systemLocaleString, localeString); + final String matchLevelString = LocaleUtils.getMatchLevelSortedString(matchLevel); + final int filesize = cursor.getInt(filesizeIndex); + // The key is sorted in lexicographic order, according to the match level, then + // the description. + final String key = matchLevelString + "." + description + "." + wordlistId; + final WordListPreference existingPref = prefMap.get(key); + if (null == existingPref || existingPref.hasPriorityOver(status)) { + final WordListPreference oldPreference = mCurrentPreferenceMap.get(key); + final WordListPreference pref; + if (null != oldPreference + && oldPreference.mVersion == version + && oldPreference.hasStatus(status) + && oldPreference.mLocale.equals(locale)) { + // If the old preference has all the new attributes, reuse it. Ideally, + // we should reuse the old pref even if its status is different and call + // setStatus here, but setStatus calls Preference#setSummary() which + // needs to be done on the UI thread and we're not on the UI thread + // here. We could do all this work on the UI thread, but in this case + // it's probably lighter to stay on a background thread and throw this + // old preference out. + pref = oldPreference; + } else { + // Otherwise, discard it and create a new one instead. + // TODO: when the status is different from the old one, we need to + // animate the old one out before animating the new one in. + pref = new WordListPreference(activity, mDictionaryListInterfaceState, + mClientId, wordlistId, version, locale, description, status, + filesize); + } + prefMap.put(key, pref); + } + } while (cursor.moveToNext()); + mCurrentPreferenceMap = prefMap; + return prefMap.values(); } finally { cursor.close(); } @@ -396,26 +385,28 @@ public final class DictionarySettingsFragment extends PreferenceFragment if (null != mUpdateNowMenu) mUpdateNowMenu.setTitle(R.string.cancel); } - private void stopLoadingAnimation() { + void stopLoadingAnimation() { final View preferenceView = getView(); final Activity activity = getActivity(); if (null == activity) return; + final View loadingView = mLoadingView; + final MenuItem updateNowMenu = mUpdateNowMenu; activity.runOnUiThread(new Runnable() { - @Override - public void run() { - mLoadingView.setVisibility(View.GONE); - preferenceView.setVisibility(View.VISIBLE); - mLoadingView.startAnimation(AnimationUtils.loadAnimation( - getActivity(), android.R.anim.fade_out)); - preferenceView.startAnimation(AnimationUtils.loadAnimation( - getActivity(), android.R.anim.fade_in)); - // The menu is created by the framework asynchronously after the activity, - // which means it's possible to have the activity running but the menu not - // created yet - hence the necessity for a null check here. - if (null != mUpdateNowMenu) { - mUpdateNowMenu.setTitle(R.string.check_for_updates_now); - } + @Override + public void run() { + loadingView.setVisibility(View.GONE); + preferenceView.setVisibility(View.VISIBLE); + loadingView.startAnimation(AnimationUtils.loadAnimation( + activity, android.R.anim.fade_out)); + preferenceView.startAnimation(AnimationUtils.loadAnimation( + activity, android.R.anim.fade_in)); + // The menu is created by the framework asynchronously after the activity, + // which means it's possible to have the activity running but the menu not + // created yet - hence the necessity for a null check here. + if (null != updateNowMenu) { + updateNowMenu.setTitle(R.string.check_for_updates_now); } - }); + } + }); } } diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java index d3c0a910f..f1633ff28 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java +++ b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java @@ -24,6 +24,7 @@ import android.view.View; import android.widget.Button; import android.widget.TextView; +import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.R; import java.util.Locale; @@ -63,11 +64,19 @@ public final class DownloadOverMeteredDialog extends Activity { allowButton.setText(String.format(allowButtonFormat, ((float)size)/(1024*1024))); } + // This method is externally referenced from layout/download_over_metered.xml using onClick + // attribute of Button. + @ExternallyReferenced + @SuppressWarnings("unused") public void onClickDeny(final View v) { UpdateHandler.setDownloadOverMeteredSetting(this, false); finish(); } + // This method is externally referenced from layout/download_over_metered.xml using onClick + // attribute of Button. + @ExternallyReferenced + @SuppressWarnings("unused") public void onClickAllow(final View v) { UpdateHandler.setDownloadOverMeteredSetting(this, true); UpdateHandler.installIfNeverRequested(this, mClientId, mWordListToDownload, diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java index c9e8f9118..db4315f8f 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java @@ -226,7 +226,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { createClientTable(db); } - private void addRawChecksumColumnUnlessPresent(final SQLiteDatabase db) { + private static void addRawChecksumColumnUnlessPresent(final SQLiteDatabase db) { try { db.execSQL("SELECT " + RAW_CHECKSUM_COLUMN + " FROM " + METADATA_TABLE_NAME + " LIMIT 0;"); @@ -237,7 +237,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { } } - private void addRetryCountColumnUnlessPresent(final SQLiteDatabase db) { + private static void addRetryCountColumnUnlessPresent(final SQLiteDatabase db) { try { db.execSQL("SELECT " + RETRY_COUNT_COLUMN + " FROM " + METADATA_TABLE_NAME + " LIMIT 0;"); diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java index 639d904a0..329b9f62e 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java @@ -30,9 +30,6 @@ import java.util.List; * Helper class to easy up manipulation of dictionary pack metadata. */ public class MetadataHandler { - @SuppressWarnings("unused") - private static final String TAG = "DictionaryProvider:" + MetadataHandler.class.getSimpleName(); - // The canonical file name for metadata. This is not the name of a real file on the // device, but a symbolic name used in the database and in metadata handling. It is never // tested against, only used for human-readability as the file name for the metadata. diff --git a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java b/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java index 67dd7b9b7..bb64721d5 100644 --- a/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java +++ b/java/src/com/android/inputmethod/dictionarypack/PrivateLog.java @@ -43,8 +43,8 @@ public class PrivateLog { + COLUMN_DATE + " TEXT," + COLUMN_EVENT + " TEXT);"; - private static final SimpleDateFormat sDateFormat = - new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); + static final SimpleDateFormat sDateFormat = new SimpleDateFormat( + "yyyy/MM/dd HH:mm:ss", Locale.ROOT); private static PrivateLog sInstance = new PrivateLog(); private static DebugHelper sDebugHelper = null; @@ -62,9 +62,9 @@ public class PrivateLog { } } - private static class DebugHelper extends SQLiteOpenHelper { + static class DebugHelper extends SQLiteOpenHelper { - private DebugHelper(final Context context) { + DebugHelper(final Context context) { super(context, LOG_DATABASE_NAME, null, LOG_DATABASE_VERSION); } @@ -84,7 +84,7 @@ public class PrivateLog { insert(db, "Upgrade finished"); } - private static void insert(SQLiteDatabase db, String event) { + static void insert(SQLiteDatabase db, String event) { if (!DEBUG) return; final ContentValues c = new ContentValues(2); c.put(COLUMN_DATE, sDateFormat.format(new Date(System.currentTimeMillis()))); diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index 90a750493..d59b7a545 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -59,6 +59,8 @@ import java.util.Locale; import java.util.Set; import java.util.TreeSet; +import javax.annotation.Nullable; + /** * Handler for the update process. * @@ -750,19 +752,22 @@ public final class UpdateHandler { * @return an ordered list of runnables to be called to upgrade. */ private static ActionBatch compareMetadataForUpgrade(final Context context, - final String clientId, List<WordListMetadata> from, List<WordListMetadata> to) { + final String clientId, @Nullable final List<WordListMetadata> from, + @Nullable final List<WordListMetadata> to) { final ActionBatch actions = new ActionBatch(); // Upgrade existing word lists DebugLogUtils.l("Comparing dictionaries"); final Set<String> wordListIds = new TreeSet<>(); // TODO: Can these be null? - if (null == from) from = new ArrayList<>(); - if (null == to) to = new ArrayList<>(); - for (WordListMetadata wlData : from) wordListIds.add(wlData.mId); - for (WordListMetadata wlData : to) wordListIds.add(wlData.mId); + final List<WordListMetadata> fromList = (from == null) ? new ArrayList<WordListMetadata>() + : from; + final List<WordListMetadata> toList = (to == null) ? new ArrayList<WordListMetadata>() + : to; + for (WordListMetadata wlData : fromList) wordListIds.add(wlData.mId); + for (WordListMetadata wlData : toList) wordListIds.add(wlData.mId); for (String id : wordListIds) { - final WordListMetadata currentInfo = MetadataHandler.findWordListById(from, id); - final WordListMetadata metadataInfo = MetadataHandler.findWordListById(to, id); + final WordListMetadata currentInfo = MetadataHandler.findWordListById(fromList, id); + final WordListMetadata metadataInfo = MetadataHandler.findWordListById(toList, id); // TODO: Remove the following unnecessary check, since we are now doing the filtering // inside findWordListById. final WordListMetadata newInfo = null == metadataInfo diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java index aea16af0d..500e39e0e 100644 --- a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java +++ b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java @@ -38,45 +38,39 @@ import java.util.Locale; * enable or delete it as appropriate for the current state of the word list. */ public final class WordListPreference extends Preference { - static final private String TAG = WordListPreference.class.getSimpleName(); + private static final String TAG = WordListPreference.class.getSimpleName(); // What to display in the "status" field when we receive unknown data as a status from // the content provider. Empty string sounds sensible. - static final private String NO_STATUS_MESSAGE = ""; + private static final String NO_STATUS_MESSAGE = ""; /// Actions - static final private int ACTION_UNKNOWN = 0; - static final private int ACTION_ENABLE_DICT = 1; - static final private int ACTION_DISABLE_DICT = 2; - static final private int ACTION_DELETE_DICT = 3; + private static final int ACTION_UNKNOWN = 0; + private static final int ACTION_ENABLE_DICT = 1; + private static final int ACTION_DISABLE_DICT = 2; + private static final int ACTION_DELETE_DICT = 3; // Members - // The context to get resources - final Context mContext; - // The id of the client for which this preference is. - final String mClientId; // The metadata word list id and version of this word list. public final String mWordlistId; public final int mVersion; public final Locale mLocale; public final String mDescription; + + // The id of the client for which this preference is. + private final String mClientId; // The status private int mStatus; // The size of the dictionary file private final int mFilesize; private final DictionaryListInterfaceState mInterfaceState; - private final OnWordListPreferenceClick mPreferenceClickHandler = - new OnWordListPreferenceClick(); - private final OnActionButtonClick mActionButtonClickHandler = - new OnActionButtonClick(); public WordListPreference(final Context context, final DictionaryListInterfaceState dictionaryListInterfaceState, final String clientId, final String wordlistId, final int version, final Locale locale, final String description, final int status, final int filesize) { super(context, null); - mContext = context; mInterfaceState = dictionaryListInterfaceState; mClientId = clientId; mVersion = version; @@ -116,22 +110,23 @@ public final class WordListPreference extends Preference { } private String getSummary(final int status) { + final Context context = getContext(); switch (status) { - // If we are deleting the word list, for the user it's like it's already deleted. - // It should be reinstallable. Exposing to the user the whole complexity of - // the delayed deletion process between the dictionary pack and Android Keyboard - // would only be confusing. - case MetadataDbHelper.STATUS_DELETING: - case MetadataDbHelper.STATUS_AVAILABLE: - return mContext.getString(R.string.dictionary_available); - case MetadataDbHelper.STATUS_DOWNLOADING: - return mContext.getString(R.string.dictionary_downloading); - case MetadataDbHelper.STATUS_INSTALLED: - return mContext.getString(R.string.dictionary_installed); - case MetadataDbHelper.STATUS_DISABLED: - return mContext.getString(R.string.dictionary_disabled); - default: - return NO_STATUS_MESSAGE; + // If we are deleting the word list, for the user it's like it's already deleted. + // It should be reinstallable. Exposing to the user the whole complexity of + // the delayed deletion process between the dictionary pack and Android Keyboard + // would only be confusing. + case MetadataDbHelper.STATUS_DELETING: + case MetadataDbHelper.STATUS_AVAILABLE: + return context.getString(R.string.dictionary_available); + case MetadataDbHelper.STATUS_DOWNLOADING: + return context.getString(R.string.dictionary_downloading); + case MetadataDbHelper.STATUS_INSTALLED: + return context.getString(R.string.dictionary_installed); + case MetadataDbHelper.STATUS_DISABLED: + return context.getString(R.string.dictionary_disabled); + default: + return NO_STATUS_MESSAGE; } } @@ -154,7 +149,7 @@ public final class WordListPreference extends Preference { { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT } }; - private int getButtonSwitcherStatus(final int status) { + static int getButtonSwitcherStatus(final int status) { if (status >= sStatusActionList.length) { Log.e(TAG, "Unknown status " + status); return ButtonSwitcher.STATUS_NO_BUTTON; @@ -162,7 +157,7 @@ public final class WordListPreference extends Preference { return sStatusActionList[status][0]; } - private static int getActionIdFromStatusAndMenuEntry(final int status) { + static int getActionIdFromStatusAndMenuEntry(final int status) { if (status >= sStatusActionList.length) { Log.e(TAG, "Unknown status " + status); return ACTION_UNKNOWN; @@ -171,9 +166,10 @@ public final class WordListPreference extends Preference { } private void disableDict() { - SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); + final Context context = getContext(); + final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context); CommonPreferences.disable(prefs, mWordlistId); - UpdateHandler.markAsUnused(mContext, mClientId, mWordlistId, mVersion, mStatus); + UpdateHandler.markAsUnused(context, mClientId, mWordlistId, mVersion, mStatus); if (MetadataDbHelper.STATUS_DOWNLOADING == mStatus) { setStatus(MetadataDbHelper.STATUS_AVAILABLE); } else if (MetadataDbHelper.STATUS_INSTALLED == mStatus) { @@ -184,11 +180,13 @@ public final class WordListPreference extends Preference { Log.e(TAG, "Unexpected state of the word list for disabling " + mStatus); } } + private void enableDict() { - SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); + final Context context = getContext(); + final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context); CommonPreferences.enable(prefs, mWordlistId); // Explicit enabling by the user : allow downloading on metered data connection. - UpdateHandler.markAsUsed(mContext, mClientId, mWordlistId, mVersion, mStatus, true); + UpdateHandler.markAsUsed(context, mClientId, mWordlistId, mVersion, mStatus, true); if (MetadataDbHelper.STATUS_AVAILABLE == mStatus) { setStatus(MetadataDbHelper.STATUS_DOWNLOADING); } else if (MetadataDbHelper.STATUS_DISABLED == mStatus @@ -203,11 +201,13 @@ public final class WordListPreference extends Preference { Log.e(TAG, "Unexpected state of the word list for enabling " + mStatus); } } + private void deleteDict() { - SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); + final Context context = getContext(); + final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context); CommonPreferences.disable(prefs, mWordlistId); setStatus(MetadataDbHelper.STATUS_DELETING); - UpdateHandler.markAsDeleting(mContext, mClientId, mWordlistId, mVersion, mStatus); + UpdateHandler.markAsDeleting(context, mClientId, mWordlistId, mVersion, mStatus); } @Override @@ -225,8 +225,8 @@ public final class WordListPreference extends Preference { status.setVisibility(showProgressBar ? View.INVISIBLE : View.VISIBLE); progressBar.setVisibility(showProgressBar ? View.VISIBLE : View.INVISIBLE); - final ButtonSwitcher buttonSwitcher = - (ButtonSwitcher)view.findViewById(R.id.wordlist_button_switcher); + final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)view.findViewById( + R.id.wordlist_button_switcher); // We need to clear the state of the button switcher, because we reuse views; if we didn't // reset it would animate from whatever its old state was. buttonSwitcher.reset(mInterfaceState); @@ -244,63 +244,67 @@ public final class WordListPreference extends Preference { // The button is closed. buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON); } - buttonSwitcher.setInternalOnClickListener(mActionButtonClickHandler); - view.setOnClickListener(mPreferenceClickHandler); + buttonSwitcher.setInternalOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View v) { + onActionButtonClicked(); + } + }); + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View v) { + onWordListClicked(v); + } + }); } - private class OnWordListPreferenceClick implements View.OnClickListener { - @Override - public void onClick(final View v) { - // Note : v is the preference view - final ViewParent parent = v.getParent(); - // Just in case something changed in the framework, test for the concrete class - if (!(parent instanceof ListView)) return; - final ListView listView = (ListView)parent; - final int indexToOpen; - // Close all first, we'll open back any item that needs to be open. - final boolean wasOpen = mInterfaceState.isOpen(mWordlistId); - mInterfaceState.closeAll(); - if (wasOpen) { - // This button being shown. Take note that we don't want to open any button in the - // loop below. - indexToOpen = -1; + void onWordListClicked(final View v) { + // Note : v is the preference view + final ViewParent parent = v.getParent(); + // Just in case something changed in the framework, test for the concrete class + if (!(parent instanceof ListView)) return; + final ListView listView = (ListView)parent; + final int indexToOpen; + // Close all first, we'll open back any item that needs to be open. + final boolean wasOpen = mInterfaceState.isOpen(mWordlistId); + mInterfaceState.closeAll(); + if (wasOpen) { + // This button being shown. Take note that we don't want to open any button in the + // loop below. + indexToOpen = -1; + } else { + // This button was not being shown. Open it, and remember the index of this + // child as the one to open in the following loop. + mInterfaceState.setOpen(mWordlistId, mStatus); + indexToOpen = listView.indexOfChild(v); + } + final int lastDisplayedIndex = + listView.getLastVisiblePosition() - listView.getFirstVisiblePosition(); + // The "lastDisplayedIndex" is actually displayed, hence the <= + for (int i = 0; i <= lastDisplayedIndex; ++i) { + final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)listView.getChildAt(i) + .findViewById(R.id.wordlist_button_switcher); + if (i == indexToOpen) { + buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus)); } else { - // This button was not being shown. Open it, and remember the index of this - // child as the one to open in the following loop. - mInterfaceState.setOpen(mWordlistId, mStatus); - indexToOpen = listView.indexOfChild(v); - } - final int lastDisplayedIndex = - listView.getLastVisiblePosition() - listView.getFirstVisiblePosition(); - // The "lastDisplayedIndex" is actually displayed, hence the <= - for (int i = 0; i <= lastDisplayedIndex; ++i) { - final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)listView.getChildAt(i) - .findViewById(R.id.wordlist_button_switcher); - if (i == indexToOpen) { - buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus)); - } else { - buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON); - } + buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON); } } } - private class OnActionButtonClick implements View.OnClickListener { - @Override - public void onClick(final View v) { - switch (getActionIdFromStatusAndMenuEntry(mStatus)) { - case ACTION_ENABLE_DICT: - enableDict(); - break; - case ACTION_DISABLE_DICT: - disableDict(); - break; - case ACTION_DELETE_DICT: - deleteDict(); - break; - default: - Log.e(TAG, "Unknown menu item pressed"); - } + void onActionButtonClicked() { + switch (getActionIdFromStatusAndMenuEntry(mStatus)) { + case ACTION_ENABLE_DICT: + enableDict(); + break; + case ACTION_DISABLE_DICT: + disableDict(); + break; + case ACTION_DELETE_DICT: + deleteDict(); + break; + default: + Log.e(TAG, "Unknown menu item pressed"); } } } diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java index 2d2731f21..5858faa09 100644 --- a/java/src/com/android/inputmethod/event/CombinerChain.java +++ b/java/src/com/android/inputmethod/event/CombinerChain.java @@ -19,7 +19,7 @@ package com.android.inputmethod.event; import android.text.SpannableStringBuilder; import android.text.TextUtils; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.ArrayList; import java.util.HashMap; @@ -97,7 +97,8 @@ public class CombinerChain { * new event. However it may never be null. */ @Nonnull - public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { + public Event processEvent(final ArrayList<Event> previousEvents, + @Nonnull final Event newEvent) { final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents); Event event = newEvent; for (final Combiner combiner : mCombiners) { diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java index 88c70630d..1a28bb1d5 100644 --- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java +++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java @@ -18,9 +18,8 @@ package com.android.inputmethod.event; import android.text.TextUtils; import android.util.SparseIntArray; -import android.view.KeyCharacterMap; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.text.Normalizer; import java.util.ArrayList; @@ -70,8 +69,8 @@ public class DeadKeyCombiner implements Combiner { /** * Maps Unicode combining diacritical to display-form dead key. */ - private static final SparseIntArray sCombiningToAccent = new SparseIntArray(); - private static final SparseIntArray sAccentToCombining = new SparseIntArray(); + static final SparseIntArray sCombiningToAccent = new SparseIntArray(); + static final SparseIntArray sAccentToCombining = new SparseIntArray(); static { // U+0300: COMBINING GRAVE ACCENT addCombining('\u0300', ACCENT_GRAVE); @@ -217,21 +216,20 @@ public class DeadKeyCombiner implements Combiner { final StringBuilder mDeadSequence = new StringBuilder(); @Nonnull - private Event createEventChainFromSequence(final @Nonnull CharSequence text, - final Event originalEvent) { - if (text.length() <= 0) { + private static Event createEventChainFromSequence(final @Nonnull CharSequence text, + @Nonnull final Event originalEvent) { + int index = text.length(); + if (index <= 0) { return originalEvent; - } else { - Event lastEvent = null; - int codePoint = 0; - for (int i = text.length(); i > 0; i -= Character.charCount(codePoint)) { - codePoint = Character.codePointBefore(text, i); - final Event thisEvent = Event.createHardwareKeypressEvent(codePoint, - originalEvent.mKeyCode, lastEvent, false /* isKeyRepeat */); - lastEvent = thisEvent; - } - return lastEvent; } + Event lastEvent = null; + do { + final int codePoint = Character.codePointBefore(text, index); + lastEvent = Event.createHardwareKeypressEvent(codePoint, + originalEvent.mKeyCode, lastEvent, false /* isKeyRepeat */); + index -= Character.charCount(codePoint); + } while (index > 0); + return lastEvent; } @Override @@ -248,51 +246,49 @@ public class DeadKeyCombiner implements Combiner { // no dead keys at all in the current input, so this combiner has nothing to do and // simply returns the event as is. The majority of events will go through this path. return event; - } else { - if (Character.isWhitespace(event.mCodePoint) - || event.mCodePoint == mDeadSequence.codePointBefore(mDeadSequence.length())) { - // When whitespace or twice the same dead key, we should output the dead sequence - // as is. - final Event resultEvent = createEventChainFromSequence(mDeadSequence.toString(), - event); - mDeadSequence.setLength(0); - return resultEvent; - } else if (event.isFunctionalKeyEvent()) { - if (Constants.CODE_DELETE == event.mKeyCode) { - // Remove the last code point - final int trimIndex = mDeadSequence.length() - Character.charCount( - mDeadSequence.codePointBefore(mDeadSequence.length())); - mDeadSequence.setLength(trimIndex); - return Event.createConsumedEvent(event); - } else { - return event; - } - } else if (event.isDead()) { - mDeadSequence.appendCodePoint(event.mCodePoint); + } + if (Character.isWhitespace(event.mCodePoint) + || event.mCodePoint == mDeadSequence.codePointBefore(mDeadSequence.length())) { + // When whitespace or twice the same dead key, we should output the dead sequence as is. + final Event resultEvent = createEventChainFromSequence(mDeadSequence.toString(), + event); + mDeadSequence.setLength(0); + return resultEvent; + } + if (event.isFunctionalKeyEvent()) { + if (Constants.CODE_DELETE == event.mKeyCode) { + // Remove the last code point + final int trimIndex = mDeadSequence.length() - Character.charCount( + mDeadSequence.codePointBefore(mDeadSequence.length())); + mDeadSequence.setLength(trimIndex); return Event.createConsumedEvent(event); + } + return event; + } + if (event.isDead()) { + mDeadSequence.appendCodePoint(event.mCodePoint); + return Event.createConsumedEvent(event); + } + // Combine normally. + final StringBuilder sb = new StringBuilder(); + sb.appendCodePoint(event.mCodePoint); + int codePointIndex = 0; + while (codePointIndex < mDeadSequence.length()) { + final int deadCodePoint = mDeadSequence.codePointAt(codePointIndex); + final char replacementSpacingChar = + Data.getNonstandardCombination(deadCodePoint, event.mCodePoint); + if (Data.NOT_A_CHAR != replacementSpacingChar) { + sb.setCharAt(0, replacementSpacingChar); } else { - // Combine normally. - final StringBuilder sb = new StringBuilder(); - sb.appendCodePoint(event.mCodePoint); - int codePointIndex = 0; - while (codePointIndex < mDeadSequence.length()) { - final int deadCodePoint = mDeadSequence.codePointAt(codePointIndex); - final char replacementSpacingChar = - Data.getNonstandardCombination(deadCodePoint, event.mCodePoint); - if (Data.NOT_A_CHAR != replacementSpacingChar) { - sb.setCharAt(0, replacementSpacingChar); - } else { - final int combining = Data.sAccentToCombining.get(deadCodePoint); - sb.appendCodePoint(0 == combining ? deadCodePoint : combining); - } - codePointIndex += Character.isSupplementaryCodePoint(deadCodePoint) ? 2 : 1; - } - final String normalizedString = Normalizer.normalize(sb, Normalizer.Form.NFC); - final Event resultEvent = createEventChainFromSequence(normalizedString, event); - mDeadSequence.setLength(0); - return resultEvent; + final int combining = Data.sAccentToCombining.get(deadCodePoint); + sb.appendCodePoint(0 == combining ? deadCodePoint : combining); } + codePointIndex += Character.isSupplementaryCodePoint(deadCodePoint) ? 2 : 1; } + final String normalizedString = Normalizer.normalize(sb, Normalizer.Form.NFC); + final Event resultEvent = createEventChainFromSequence(normalizedString, event); + mDeadSequence.setLength(0); + return resultEvent; } @Override diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java index ff6f88066..a1226dc93 100644 --- a/java/src/com/android/inputmethod/event/Event.java +++ b/java/src/com/android/inputmethod/event/Event.java @@ -17,9 +17,11 @@ package com.android.inputmethod.event; import com.android.inputmethod.annotations.ExternallyReferenced; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; + +import javax.annotation.Nonnull; /** * Class representing a generic input event as handled by Latin IME. @@ -134,12 +136,14 @@ public class Event { } } + @Nonnull public static Event createSoftwareKeypressEvent(final int codePoint, final int keyCode, final int x, final int y, final boolean isKeyRepeat) { return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, x, y, null /* suggestedWordInfo */, isKeyRepeat ? FLAG_REPEAT : FLAG_NONE, null); } + @Nonnull public static Event createHardwareKeypressEvent(final int codePoint, final int keyCode, final Event next, final boolean isKeyRepeat) { return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, @@ -149,6 +153,7 @@ public class Event { // This creates an input event for a dead character. @see {@link #FLAG_DEAD} @ExternallyReferenced + @Nonnull public static Event createDeadEvent(final int codePoint, final int keyCode, final Event next) { // TODO: add an argument or something if we ever create a software layout with dead keys. return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, @@ -163,6 +168,7 @@ public class Event { * @param codePoint the code point. * @return an event for this code point. */ + @Nonnull public static Event createEventForCodePointFromUnknownSource(final int codePoint) { // TODO: should we have a different type of event for this? After all, it's not a key press. return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE, @@ -178,6 +184,7 @@ public class Event { * @param y the Y coordinate. * @return an event for this code point and coordinates. */ + @Nonnull public static Event createEventForCodePointFromAlreadyTypedText(final int codePoint, final int x, final int y) { // TODO: should we have a different type of event for this? After all, it's not a key press. @@ -189,6 +196,7 @@ public class Event { * Creates an input event representing the manual pick of a suggestion. * @return an event for this suggestion pick. */ + @Nonnull public static Event createSuggestionPickedEvent(final SuggestedWordInfo suggestedWordInfo) { return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, NOT_A_CODE_POINT, NOT_A_KEY_CODE, @@ -204,6 +212,7 @@ public class Event { * @param keyCode the key code, or NOT_A_KEYCODE if not applicable. * @return an event for this text. */ + @Nonnull public static Event createSoftwareTextEvent(final CharSequence text, final int keyCode) { return new Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, @@ -214,6 +223,7 @@ public class Event { * Creates an input event representing the manual pick of a punctuation suggestion. * @return an event for this suggestion pick. */ + @Nonnull public static Event createPunctuationSuggestionPickedEvent( final SuggestedWordInfo suggestedWordInfo) { final int primaryCode = suggestedWordInfo.mWord.charAt(0); @@ -228,6 +238,7 @@ public class Event { * @param source the event to copy the properties of. * @return an identical event marked as consumed. */ + @Nonnull public static Event createConsumedEvent(final Event source) { // A consumed event should not input any text at all, so we pass the empty string as text. return new Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, @@ -235,6 +246,7 @@ public class Event { source.mNextEvent); } + @Nonnull public static Event createNotHandledEvent() { return new Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, diff --git a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java index c61f45efa..3a4097d7f 100644 --- a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java +++ b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java @@ -19,7 +19,7 @@ package com.android.inputmethod.event; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; /** * A hardware event decoder for a hardware qwerty-ish keyboard. @@ -67,10 +67,9 @@ public class HardwareKeyboardEventDecoder implements HardwareEventDecoder { if (keyEvent.isShiftPressed()) { return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, Constants.CODE_SHIFT_ENTER, null /* next */, isKeyRepeat); - } else { - return Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode, - null /* next */, isKeyRepeat); } + return Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode, + null /* next */, isKeyRepeat); } // If not Enter, then this is just a regular keypress event for a normal character // that can be committed right away, taking into account the current state. diff --git a/java/src/com/android/inputmethod/event/MyanmarReordering.java b/java/src/com/android/inputmethod/event/MyanmarReordering.java index dcd06c899..7bc1630f5 100644 --- a/java/src/com/android/inputmethod/event/MyanmarReordering.java +++ b/java/src/com/android/inputmethod/event/MyanmarReordering.java @@ -16,7 +16,7 @@ package com.android.inputmethod.event; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.ArrayList; import java.util.Arrays; @@ -115,6 +115,7 @@ public class MyanmarReordering implements Combiner { * @param newEvent the new event to add to the stream. null if none. * @return the resulting software text event. Never null. */ + @Nonnull private Event clearAndGetResultingEvent(final Event newEvent) { final CharSequence combinedText; if (mCurrentEvents.size() > 0) { @@ -139,7 +140,8 @@ public class MyanmarReordering implements Combiner { if (null == lastEvent) { mCurrentEvents.add(newEvent); return Event.createConsumedEvent(newEvent); - } else if (isConsonantOrMedial(lastEvent.mCodePoint)) { + } + if (isConsonantOrMedial(lastEvent.mCodePoint)) { final Event resultingEvent = clearAndGetResultingEvent(null); mCurrentEvents.add(Event.createSoftwareKeypressEvent(ZERO_WIDTH_NON_JOINER, Event.NOT_A_KEY_CODE, @@ -147,15 +149,17 @@ public class MyanmarReordering implements Combiner { false /* isKeyRepeat */)); mCurrentEvents.add(newEvent); return resultingEvent; - } else { // VOWEL_E == lastCodePoint. But if that was anything else this is correct too. - return clearAndGetResultingEvent(newEvent); } - } if (isConsonant(codePoint)) { + // VOWEL_E == lastCodePoint. But if that was anything else this is correct too. + return clearAndGetResultingEvent(newEvent); + } + if (isConsonant(codePoint)) { final Event lastEvent = getLastEvent(); if (null == lastEvent) { mCurrentEvents.add(newEvent); return Event.createConsumedEvent(newEvent); - } else if (VOWEL_E == lastEvent.mCodePoint) { + } + if (VOWEL_E == lastEvent.mCodePoint) { final int eventSize = mCurrentEvents.size(); if (eventSize >= 2 && mCurrentEvents.get(eventSize - 2).mCodePoint == ZERO_WIDTH_NON_JOINER) { @@ -178,15 +182,17 @@ public class MyanmarReordering implements Combiner { mCurrentEvents.add(newEvent); mCurrentEvents.add(lastEvent); return Event.createConsumedEvent(newEvent); - } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine - return clearAndGetResultingEvent(newEvent); } - } else if (isMedial(codePoint)) { + // lastCodePoint is a consonant/medial. But if it's something else it's fine + return clearAndGetResultingEvent(newEvent); + } + if (isMedial(codePoint)) { final Event lastEvent = getLastEvent(); if (null == lastEvent) { mCurrentEvents.add(newEvent); return Event.createConsumedEvent(newEvent); - } else if (VOWEL_E == lastEvent.mCodePoint) { + } + if (VOWEL_E == lastEvent.mCodePoint) { final int eventSize = mCurrentEvents.size(); // If there is already a consonant, then we are in the middle of a syllable, and we // need to reorder. @@ -205,37 +211,36 @@ public class MyanmarReordering implements Combiner { } // Otherwise, we just commit everything. return clearAndGetResultingEvent(null); - } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine - return clearAndGetResultingEvent(newEvent); } - } else if (Constants.CODE_DELETE == newEvent.mKeyCode) { - final Event lastEvent = getLastEvent(); + // lastCodePoint is a consonant/medial. But if it's something else it's fine + return clearAndGetResultingEvent(newEvent); + } + final Event lastEvent = getLastEvent(); + if (Constants.CODE_DELETE == newEvent.mKeyCode && null != lastEvent) { final int eventSize = mCurrentEvents.size(); - if (null != lastEvent) { - if (VOWEL_E == lastEvent.mCodePoint) { - // We have a VOWEL_E at the end. There are four cases. - // - The vowel is the only code point in the buffer. Remove it. - // - The vowel is preceded by a ZWNJ. Remove both vowel E and ZWNJ. - // - The vowel is preceded by a consonant/medial, remove the consonant/medial. - // - In all other cases, it's strange, so just remove the last code point. - if (eventSize <= 1) { - mCurrentEvents.clear(); - } else { // eventSize >= 2 - final int previousCodePoint = mCurrentEvents.get(eventSize - 2).mCodePoint; - if (previousCodePoint == ZERO_WIDTH_NON_JOINER) { - mCurrentEvents.remove(eventSize - 1); - mCurrentEvents.remove(eventSize - 2); - } else if (isConsonantOrMedial(previousCodePoint)) { - mCurrentEvents.remove(eventSize - 2); - } else { - mCurrentEvents.remove(eventSize - 1); - } + if (VOWEL_E == lastEvent.mCodePoint) { + // We have a VOWEL_E at the end. There are four cases. + // - The vowel is the only code point in the buffer. Remove it. + // - The vowel is preceded by a ZWNJ. Remove both vowel E and ZWNJ. + // - The vowel is preceded by a consonant/medial, remove the consonant/medial. + // - In all other cases, it's strange, so just remove the last code point. + if (eventSize <= 1) { + mCurrentEvents.clear(); + } else { // eventSize >= 2 + final int previousCodePoint = mCurrentEvents.get(eventSize - 2).mCodePoint; + if (previousCodePoint == ZERO_WIDTH_NON_JOINER) { + mCurrentEvents.remove(eventSize - 1); + mCurrentEvents.remove(eventSize - 2); + } else if (isConsonantOrMedial(previousCodePoint)) { + mCurrentEvents.remove(eventSize - 2); + } else { + mCurrentEvents.remove(eventSize - 1); } - return Event.createConsumedEvent(newEvent); - } else if (eventSize > 0) { - mCurrentEvents.remove(eventSize - 1); - return Event.createConsumedEvent(newEvent); } + return Event.createConsumedEvent(newEvent); + } else if (eventSize > 0) { + mCurrentEvents.remove(eventSize - 1); + return Event.createConsumedEvent(newEvent); } } // This character is not part of the combining scheme, so we should reset everything. @@ -243,10 +248,9 @@ public class MyanmarReordering implements Combiner { // If we have events in flight, then add the new event and return the resulting event. mCurrentEvents.add(newEvent); return clearAndGetResultingEvent(null); - } else { - // If we don't have any events in flight, then just pass this one through. - return newEvent; } + // If we don't have any events in flight, then just pass this one through. + return newEvent; } @Override diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 45ce6a85f..6b2094b9e 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -17,10 +17,10 @@ package com.android.inputmethod.keyboard; import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; -import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT; -import static com.android.inputmethod.latin.Constants.CODE_SHIFT; -import static com.android.inputmethod.latin.Constants.CODE_SWITCH_ALPHA_SYMBOL; -import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; +import static com.android.inputmethod.latin.common.Constants.CODE_OUTPUT_TEXT; +import static com.android.inputmethod.latin.common.Constants.CODE_SHIFT; +import static com.android.inputmethod.latin.common.Constants.CODE_SWITCH_ALPHA_SYMBOL; +import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED; import android.content.res.TypedArray; import android.graphics.Rect; @@ -36,9 +36,9 @@ import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.KeyboardRow; import com.android.inputmethod.keyboard.internal.MoreKeySpec; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index d35c8fae1..3c90a04db 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -21,7 +21,7 @@ import android.util.SparseArray; import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.CoordinateUtils; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index c565866b7..cdd632bc8 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -16,8 +16,8 @@ package com.android.inputmethod.keyboard; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.InputPointers; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; public interface KeyboardActionListener { /** diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index ab0d63306..d43bf37cb 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; import android.text.InputType; import android.text.TextUtils; @@ -25,7 +25,6 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.utils.InputTypeUtils; -import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Arrays; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 1c66c37d3..7eb91b588 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -16,8 +16,8 @@ package com.android.inputmethod.keyboard; -import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_SETTINGS_KEY; +import static com.android.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_SETTINGS_KEY; import android.content.Context; import android.content.res.Resources; @@ -40,7 +40,6 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -360,7 +359,7 @@ public final class KeyboardLayoutSet { try { final int scriptId = featureAttr.getInt(R.styleable.KeyboardLayoutSet_Feature_supportedScript, - ScriptUtils.SCRIPT_UNKNOWN); + ScriptUtils.SCRIPT_UNKNOWN); XmlParseUtils.checkEndTag(TAG_FEATURE, parser); return scriptId; } finally { @@ -424,9 +423,8 @@ public final class KeyboardLayoutSet { final String tag = parser.getName(); if (TAG_KEYBOARD_SET.equals(tag)) { break; - } else { - throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_KEYBOARD_SET); } + throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_KEYBOARD_SET); } } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 93123d1ec..c2862f59d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -39,6 +39,8 @@ import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.define.ProductionFlags; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; +import com.android.inputmethod.latin.utils.CapsModeUtils; +import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.ScriptUtils; @@ -110,7 +112,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mThemeContext, editorInfo); final Resources res = mThemeContext.getResources(); final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); - final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); + final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues); builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); @@ -204,42 +206,64 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setAlphabetKeyboard"); + } setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetManualShiftedKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setAlphabetManualShiftedKeyboard"); + } setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetAutomaticShiftedKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setAlphabetAutomaticShiftedKeyboard"); + } setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetShiftLockedKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setAlphabetShiftLockedKeyboard"); + } setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetShiftLockShiftedKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setAlphabetShiftLockShiftedKeyboard"); + } setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setSymbolsKeyboard"); + } setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); } private void setMainKeyboardFrame(final SettingsValues settingsValues) { - mMainKeyboardFrame.setVisibility( - settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE); + final int visibility = settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE; + mKeyboardView.setVisibility(visibility); + // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. + // @see #getVisibleKeyboardView() and + // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets) + mMainKeyboardFrame.setVisibility(visibility); mEmojiPalettesView.setVisibility(View.GONE); mEmojiPalettesView.stopEmojiPalettes(); } @@ -247,8 +271,15 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void setEmojiKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setEmojiKeyboard"); + } final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); mMainKeyboardFrame.setVisibility(View.GONE); + // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. + // @see #getVisibleKeyboardView() and + // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets) + mKeyboardView.setVisibility(View.GONE); mEmojiPalettesView.startEmojiPalettes( mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL), mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet); @@ -269,18 +300,29 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsShiftedKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setSymbolsShiftedKeyboard"); + } setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); } // Future method for requesting an updating to the shift state. - public void requestUpdatingShiftState(final int currentAutoCapsState, - final int currentRecapitalizeState) { - mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState); + @Override + public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode) { + if (DEBUG_ACTION) { + Log.d(TAG, "requestUpdatingShiftState: " + + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags) + + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode)); + } + mState.onUpdateShiftState(autoCapsFlags, recapitalizeMode); } // Implements {@link KeyboardState.SwitchActions}. @Override public void startDoubleTapShiftKeyTimer() { + if (DEBUG_TIMER_ACTION) { + Log.d(TAG, "startDoubleTapShiftKeyTimer"); + } final MainKeyboardView keyboardView = getMainKeyboardView(); if (keyboardView != null) { keyboardView.startDoubleTapShiftKeyTimer(); @@ -290,6 +332,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void cancelDoubleTapShiftKeyTimer() { + if (DEBUG_TIMER_ACTION) { + Log.d(TAG, "setAlphabetKeyboard"); + } final MainKeyboardView keyboardView = getMainKeyboardView(); if (keyboardView != null) { keyboardView.cancelDoubleTapShiftKeyTimer(); @@ -299,6 +344,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public boolean isInDoubleTapShiftKeyTimeout() { + if (DEBUG_TIMER_ACTION) { + Log.d(TAG, "isInDoubleTapShiftKeyTimeout"); + } final MainKeyboardView keyboardView = getMainKeyboardView(); return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout(); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 8a9688ac4..006d08696 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.SharedPreferences; +import android.os.Build; import android.os.Build.VERSION_CODES; import android.preference.PreferenceManager; import android.util.Log; @@ -54,7 +55,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { VERSION_CODES.ICE_CREAM_SANDWICH), new KeyboardTheme(THEME_ID_LXX_LIGHT, "LXXLight", R.style.KeyboardTheme_LXX_Light, // Default theme for LXX. - BuildCompatUtils.VERSION_CODES_LXX), + Build.VERSION_CODES.LOLLIPOP), new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark, // This has never been selected as default theme. VERSION_CODES.BASE), diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 98cd1da54..b07693c76 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -35,12 +35,14 @@ import android.view.View; import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.TypefaceUtils; import java.util.HashSet; +import javax.annotation.Nullable; + /** * A view that renders a virtual {@link Keyboard}. * @@ -557,9 +559,10 @@ public class KeyboardView extends View { * @param key key in the attached {@link Keyboard}. * @see #invalidateAllKeys */ - public void invalidateKey(final Key key) { - if (mInvalidateAllKeys) return; - if (key == null) return; + public void invalidateKey(@Nullable final Key key) { + if (key == null || mInvalidateAllKeys) { + return; + } mInvalidatedKeys.add(key); final int x = key.getX() + getPaddingLeft(); final int y = key.getY() + getPaddingTop(); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 06f9ced92..1bad7cbb6 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -39,8 +39,8 @@ import android.view.ViewGroup; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate; import com.android.inputmethod.annotations.ExternallyReferenced; -import com.android.inputmethod.keyboard.internal.DrawingHandler; import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView; +import com.android.inputmethod.keyboard.internal.DrawingProxy; import com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview; import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview; import com.android.inputmethod.keyboard.internal.KeyDrawParams; @@ -52,18 +52,21 @@ import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; import com.android.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview; import com.android.inputmethod.keyboard.internal.TimerHandler; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; import java.util.Locale; import java.util.WeakHashMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * A view that is responsible for detecting key presses and touch movements. * @@ -107,8 +110,8 @@ import java.util.WeakHashMap; * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration */ -public final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy, - MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks { +public final class MainKeyboardView extends KeyboardView implements DrawingProxy, + MoreKeysPanel.Controller { private static final String TAG = MainKeyboardView.class.getSimpleName(); /** Listener for {@link KeyboardActionListener}. */ @@ -164,11 +167,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final KeyDetector mKeyDetector; private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper; - private final TimerHandler mKeyTimerHandler; + private final TimerHandler mTimerHandler; private final int mLanguageOnSpacebarHorizontalMargin; - private final DrawingHandler mDrawingHandler = new DrawingHandler(this); - private MainKeyboardAccessibilityDelegate mAccessibilityDelegate; public MainKeyboardView(final Context context, final AttributeSet attrs) { @@ -178,7 +179,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); - mDrawingPreviewPlacerView = new DrawingPreviewPlacerView(context, attrs); + final DrawingPreviewPlacerView drawingPreviewPlacerView = + new DrawingPreviewPlacerView(context, attrs); final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes( attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView); @@ -186,7 +188,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0); - mKeyTimerHandler = new TimerHandler( + mTimerHandler = new TimerHandler( this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime); final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension( @@ -196,7 +198,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mKeyDetector = new KeyDetector( keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier); - PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */); + PointerTracker.init(mainKeyboardViewAttr, mTimerHandler, this /* DrawingProxy */); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final boolean forceNonDistinctMultitouch = prefs.getBoolean( @@ -246,15 +248,17 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mGestureFloatingTextDrawingPreview = new GestureFloatingTextDrawingPreview( mainKeyboardViewAttr); - mGestureFloatingTextDrawingPreview.setDrawingView(mDrawingPreviewPlacerView); + mGestureFloatingTextDrawingPreview.setDrawingView(drawingPreviewPlacerView); mGestureTrailsDrawingPreview = new GestureTrailsDrawingPreview(mainKeyboardViewAttr); - mGestureTrailsDrawingPreview.setDrawingView(mDrawingPreviewPlacerView); + mGestureTrailsDrawingPreview.setDrawingView(drawingPreviewPlacerView); mSlidingKeyInputDrawingPreview = new SlidingKeyInputDrawingPreview(mainKeyboardViewAttr); - mSlidingKeyInputDrawingPreview.setDrawingView(mDrawingPreviewPlacerView); + mSlidingKeyInputDrawingPreview.setDrawingView(drawingPreviewPlacerView); mainKeyboardViewAttr.recycle(); + mDrawingPreviewPlacerView = drawingPreviewPlacerView; + final LayoutInflater inflater = LayoutInflater.from(getContext()); mMoreKeysKeyboardContainer = inflater.inflate(moreKeysKeyboardLayoutId, null); mMoreKeysKeyboardForActionContainer = inflater.inflate( @@ -307,17 +311,24 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack animatorToStart.setCurrentPlayTime(startTime); } - // Implements {@link TimerHander.Callbacks} method. - @Override - public void startWhileTypingFadeinAnimation() { - cancelAndStartAnimators( - mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator); - } - + // Implements {@link DrawingProxy#startWhileTypingAnimation(int)}. + /** + * Called when a while-typing-animation should be started. + * @param fadeInOrOut {@link DrawingProxy#FADE_IN} starts while-typing-fade-in animation. + * {@link DrawingProxy#FADE_OUT} starts while-typing-fade-out animation. + */ @Override - public void startWhileTypingFadeoutAnimation() { - cancelAndStartAnimators( - mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator); + public void startWhileTypingAnimation(final int fadeInOrOut) { + switch (fadeInOrOut) { + case DrawingProxy.FADE_IN: + cancelAndStartAnimators( + mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator); + break; + case DrawingProxy.FADE_OUT: + cancelAndStartAnimators( + mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator); + break; + } } @ExternallyReferenced @@ -379,7 +390,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public void setKeyboard(final Keyboard keyboard) { // Remove any pending messages, except dismissing preview and key repeat. - mKeyTimerHandler.cancelLongPressTimers(); + mTimerHandler.cancelLongPressTimers(); super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); @@ -451,17 +462,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack windowContentView.addView(mDrawingPreviewPlacerView); } - // Implements {@link DrawingHandler.Callbacks} method. @Override - public void dismissAllKeyPreviews() { - mKeyPreviewChoreographer.dismissAllKeyPreviews(); - PointerTracker.setReleasedKeyGraphicsToAllKeys(); - } - - @Override - public void showKeyPreview(final Key key) { + public void showKeyPreview(@Nonnull final Key key) { // If the key is invalid or has no key preview, we must not show key preview. - if (key == null || key.noKeyPreview()) { + if (key.noKeyPreview()) { return; } final Keyboard keyboard = getKeyboard(); @@ -480,22 +484,21 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack getWidth(), mOriginCoords, mDrawingPreviewPlacerView, isHardwareAccelerated()); } - // Implements {@link TimerHandler.Callbacks} method. + // Implements {@link DrawingProxy#dismissKeyPreviewWithoutDelay(Key)}. @Override - public void dismissKeyPreviewWithoutDelay(final Key key) { + public void dismissKeyPreviewWithoutDelay(@Nonnull final Key key) { mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */); - // To redraw key top letter. invalidateKey(key); } @Override - public void dismissKeyPreview(final Key key) { - if (!isHardwareAccelerated()) { - // TODO: Implement preference option to control key preview method and duration. - mDrawingHandler.dismissKeyPreview(mKeyPreviewDrawParams.getLingerTimeout(), key); + public void dismissKeyPreview(@Nonnull final Key key) { + if (isHardwareAccelerated()) { + mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */); return; } - mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */); + // TODO: Implement preference option to control key preview method and duration. + mTimerHandler.postDismissKeyPreview(key, mKeyPreviewDrawParams.getLingerTimeout()); } public void setSlidingKeyInputPreviewEnabled(final boolean enabled) { @@ -503,14 +506,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } @Override - public void showSlidingKeyInputPreview(final PointerTracker tracker) { + public void showSlidingKeyInputPreview(@Nullable final PointerTracker tracker) { locatePreviewPlacerView(); - mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker); - } - - @Override - public void dismissSlidingKeyInputPreview() { - mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); + if (tracker != null) { + mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker); + } else { + mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); + } } private void setGesturePreviewMode(final boolean isGestureTrailEnabled, @@ -519,20 +521,26 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mGestureTrailsDrawingPreview.setPreviewEnabled(isGestureTrailEnabled); } - // Implements {@link DrawingHandler.Callbacks} method. - @Override - public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) { + public void showGestureFloatingPreviewText(@Nonnull final SuggestedWords suggestedWords, + final boolean dismissDelayed) { locatePreviewPlacerView(); - mGestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords); + final GestureFloatingTextDrawingPreview gestureFloatingTextDrawingPreview = + mGestureFloatingTextDrawingPreview; + gestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords); + if (dismissDelayed) { + mTimerHandler.postDismissGestureFloatingPreviewText( + mGestureFloatingPreviewTextLingerTimeout); + } } - public void dismissGestureFloatingPreviewText() { - locatePreviewPlacerView(); - mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout); + // Implements {@link DrawingProxy#dismissGestureFloatingPreviewTextWithoutDelay()}. + @Override + public void dismissGestureFloatingPreviewTextWithoutDelay() { + mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText(); } @Override - public void showGestureTrail(final PointerTracker tracker, + public void showGestureTrail(@Nonnull final PointerTracker tracker, final boolean showsFloatingPreviewText) { locatePreviewPlacerView(); if (showsFloatingPreviewText) { @@ -599,13 +607,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return moreKeysKeyboardView; } - // Implements {@link TimerHandler.Callbacks} method. + // Implements {@link DrawingProxy@onLongPress(PointerTracker)}. /** * Called when a key is long pressed. * @param tracker the pointer tracker which pressed the parent key */ @Override - public void onLongPress(final PointerTracker tracker) { + public void onLongPress(@Nonnull final PointerTracker tracker) { if (isShowingMoreKeysPanel()) { return; } @@ -660,7 +668,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener); tracker.onShowMoreKeysPanel(moreKeysPanel); // TODO: Implement zoom in animation of more keys panel. - dismissKeyPreviewWithoutDelay(key); + mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */); } public boolean isInDraggingFinger() { @@ -673,6 +681,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public void onShowMoreKeysPanel(final MoreKeysPanel panel) { locatePreviewPlacerView(); + // Dismiss another {@link MoreKeysPanel} that may be being showed. + onDismissMoreKeysPanel(); + // Dismiss all key previews that may be being showed. + PointerTracker.setReleasedKeyGraphicsToAllKeys(); + // Dismiss sliding key input preview that may be being showed. + mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); panel.showInParent(mDrawingPreviewPlacerView); mMoreKeysPanel = panel; } @@ -695,15 +709,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } public void startDoubleTapShiftKeyTimer() { - mKeyTimerHandler.startDoubleTapShiftKeyTimer(); + mTimerHandler.startDoubleTapShiftKeyTimer(); } public void cancelDoubleTapShiftKeyTimer() { - mKeyTimerHandler.cancelDoubleTapShiftKeyTimer(); + mTimerHandler.cancelDoubleTapShiftKeyTimer(); } public boolean isInDoubleTapShiftKeyTimeout() { - return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout(); + return mTimerHandler.isInDoubleTapShiftKeyTimeout(); } @Override @@ -712,9 +726,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return false; } if (mNonDistinctMultitouchHelper != null) { - if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) { + if (me.getPointerCount() > 1 && mTimerHandler.isInKeyRepeat()) { // Key repeating timer will be canceled if 2 or more keys are in action. - mKeyTimerHandler.cancelKeyRepeatTimers(); + mTimerHandler.cancelKeyRepeatTimers(); } // Non distinct multitouch screen support mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector); @@ -738,11 +752,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } public void cancelAllOngoingEvents() { - mKeyTimerHandler.cancelAllMessages(); - mDrawingHandler.cancelAllMessages(); - dismissAllKeyPreviews(); - dismissGestureFloatingPreviewText(); - dismissSlidingKeyInputPreview(); + mTimerHandler.cancelAllMessages(); + PointerTracker.setReleasedKeyGraphicsToAllKeys(); + mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText(); + mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); PointerTracker.dismissAllMoreKeysPanels(); PointerTracker.cancelAllPointerTrackers(); } diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index abcfff8a6..f0de86ff9 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -24,7 +24,7 @@ import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreKeysKeyboard extends Keyboard { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 841283b7f..01522536f 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -29,8 +29,8 @@ import android.view.ViewGroup; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate; import com.android.inputmethod.keyboard.internal.KeyDrawParams; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.CoordinateUtils; /** diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 49288ade4..41eb87f81 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -25,15 +25,17 @@ import android.view.MotionEvent; import com.android.inputmethod.keyboard.internal.BatchInputArbiter; import com.android.inputmethod.keyboard.internal.BatchInputArbiter.BatchInputArbiterListener; import com.android.inputmethod.keyboard.internal.BogusMoveEventDetector; +import com.android.inputmethod.keyboard.internal.DrawingProxy; import com.android.inputmethod.keyboard.internal.GestureEnabler; import com.android.inputmethod.keyboard.internal.GestureStrokeDrawingParams; import com.android.inputmethod.keyboard.internal.GestureStrokeDrawingPoints; import com.android.inputmethod.keyboard.internal.GestureStrokeRecognitionParams; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; +import com.android.inputmethod.keyboard.internal.TimerProxy; import com.android.inputmethod.keyboard.internal.TypingTimeRecorder; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.CoordinateUtils; @@ -41,6 +43,9 @@ import com.android.inputmethod.latin.utils.ResourceUtils; import java.util.ArrayList; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public final class PointerTracker implements PointerTrackerQueue.Element, BatchInputArbiterListener { private static final String TAG = PointerTracker.class.getSimpleName(); @@ -49,60 +54,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private static final boolean DEBUG_LISTENER = false; private static boolean DEBUG_MODE = DebugFlags.DEBUG_ENABLED || DEBUG_EVENT; - public interface DrawingProxy { - public void invalidateKey(Key key); - public void showKeyPreview(Key key); - public void dismissKeyPreview(Key key); - public void showSlidingKeyInputPreview(PointerTracker tracker); - public void dismissSlidingKeyInputPreview(); - public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText); - } - - public interface TimerProxy { - public void startTypingStateTimer(Key typedKey); - public boolean isTypingState(); - public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay); - public void startLongPressTimerOf(PointerTracker tracker, int delay); - public void cancelLongPressTimerOf(PointerTracker tracker); - public void cancelLongPressShiftKeyTimers(); - public void cancelKeyTimersOf(PointerTracker tracker); - public void startDoubleTapShiftKeyTimer(); - public void cancelDoubleTapShiftKeyTimer(); - public boolean isInDoubleTapShiftKeyTimeout(); - public void startUpdateBatchInputTimer(PointerTracker tracker); - public void cancelUpdateBatchInputTimer(PointerTracker tracker); - public void cancelAllUpdateBatchInputTimers(); - - public static class Adapter implements TimerProxy { - @Override - public void startTypingStateTimer(Key typedKey) {} - @Override - public boolean isTypingState() { return false; } - @Override - public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay) {} - @Override - public void startLongPressTimerOf(PointerTracker tracker, int delay) {} - @Override - public void cancelLongPressTimerOf(PointerTracker tracker) {} - @Override - public void cancelLongPressShiftKeyTimers() {} - @Override - public void cancelKeyTimersOf(PointerTracker tracker) {} - @Override - public void startDoubleTapShiftKeyTimer() {} - @Override - public void cancelDoubleTapShiftKeyTimer() {} - @Override - public boolean isInDoubleTapShiftKeyTimeout() { return false; } - @Override - public void startUpdateBatchInputTimer(PointerTracker tracker) {} - @Override - public void cancelUpdateBatchInputTimer(PointerTracker tracker) {} - @Override - public void cancelAllUpdateBatchInputTimers() {} - } - } - static final class PointerTrackerParams { public final boolean mKeySelectionByDraggingFinger; public final int mTouchNoiseThresholdTime; @@ -163,6 +114,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, // The position and time at which first down event occurred. private long mDownTime; + @Nonnull private int[] mDownCoordinates = CoordinateUtils.newInstance(); private long mUpTime; @@ -416,6 +368,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mIsInDraggingFinger; } + @Nullable public Key getKey() { return mCurrentKey; } @@ -429,12 +382,12 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mKeyDetector.detectHitKey(x, y); } - private void setReleasedKeyGraphics(final Key key) { - sDrawingProxy.dismissKeyPreview(key); + private void setReleasedKeyGraphics(@Nullable final Key key) { if (key == null) { return; } + sDrawingProxy.dismissKeyPreview(key); // Even if the key is disabled, update the key release graphics just in case. updateReleaseKeyGraphics(key); @@ -518,7 +471,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mGestureStrokeDrawingPoints; } - public void getLastCoordinates(final int[] outCoords) { + public void getLastCoordinates(@Nonnull final int[] outCoords) { CoordinateUtils.set(outCoords, mLastX, mLastY); } @@ -526,7 +479,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, return mDownTime; } - public void getDownCoordinates(final int[] outCoords) { + public void getDownCoordinates(@Nonnull final int[] outCoords) { CoordinateUtils.copy(outCoords, mDownCoordinates); } @@ -575,7 +528,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, } sListener.onStartBatchInput(); dismissAllMoreKeysPanels(); - sTimerProxy.cancelLongPressTimerOf(this); + sTimerProxy.cancelLongPressTimersOf(this); } private void showGestureTrail() { @@ -765,7 +718,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private void resetKeySelectionByDraggingFinger() { mIsInDraggingFinger = false; mIsInSlidingKeyInput = false; - sDrawingProxy.dismissSlidingKeyInputPreview(); + sDrawingProxy.showSlidingKeyInputPreview(null /* tracker */); } private void onGestureMoveEvent(final int x, final int y, final long eventTime, @@ -1083,7 +1036,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, } public void cancelLongPressTimer() { - sTimerProxy.cancelLongPressTimerOf(this); + sTimerProxy.cancelLongPressTimersOf(this); } public void onLongPressed() { @@ -1152,7 +1105,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private void startLongPressTimer(final Key key) { // Note that we need to cancel all active long press shift key timers if any whenever we // start a new long press timer for both non-shift and shift keys. - sTimerProxy.cancelLongPressShiftKeyTimers(); + sTimerProxy.cancelLongPressShiftKeyTimer(); if (sInGesture) return; if (key == null) return; if (!key.isLongPressEnabled()) return; diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 9c5abcd71..ab2323b06 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -17,11 +17,10 @@ package com.android.inputmethod.keyboard; import android.graphics.Rect; -import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.keyboard.internal.TouchPositionCorrection; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.JniUtils; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java index 2b7d2ce3c..892d36752 100644 --- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java +++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java @@ -74,7 +74,7 @@ public class TextDecorator { void onClickComposingTextToAddToDictionary(final String word); } - public TextDecorator(final Listener listener) { + public TextDecorator(@Nullable final Listener listener) { mListener = (listener != null) ? listener : EMPTY_LISTENER; } @@ -83,7 +83,7 @@ public class TextDecorator { * delegated to the associated UI operator. * @param uiOperator the UI operator to be associated. */ - public void setUiOperator(final TextDecoratorUiOperator uiOperator) { + public void setUiOperator(@Nonnull final TextDecoratorUiOperator uiOperator) { mUiOperator.disposeUi(); mUiOperator = uiOperator; mUiOperator.setOnClickListener(getOnClickHandler()); @@ -181,7 +181,7 @@ public class TextDecorator { layoutMain(); } - private void layoutMain() { + void layoutMain() { final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper; if (info == null) { @@ -289,7 +289,7 @@ public class TextDecorator { mHasRtlCharsInLastComposingText); } - private void onClickIndicator() { + void onClickIndicator() { if (mMode != MODE_SHOWING_INDICATOR) { return; } @@ -347,12 +347,14 @@ public class TextDecorator { } } + @Nonnull private final static Listener EMPTY_LISTENER = new Listener() { @Override public void onClickComposingTextToAddToDictionary(final String word) { } }; + @Nonnull private final static TextDecoratorUiOperator EMPTY_UI_OPERATOR = new TextDecoratorUiOperator() { @Override public void disposeUi() { diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java index 0f9dc855b..a9711aed2 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java @@ -29,7 +29,6 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardLayoutSet; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.settings.Settings; @@ -147,7 +146,7 @@ final class EmojiCategory { mShownCategories.add(properties); } - public String getCategoryName(final int categoryId, final int categoryPageId) { + public static String getCategoryName(final int categoryId, final int categoryPageId) { return sCategoryName[categoryId] + "-" + categoryPageId; } @@ -271,7 +270,7 @@ final class EmojiCategory { } private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) { - return (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id; + return (((long) categoryId) << Integer.SIZE) | id; } public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) { diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java index 925ec6bfb..09313f811 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java @@ -138,6 +138,21 @@ final class EmojiPageKeyboardView extends KeyboardView implements return mKeyDetector.detectHitKey(x, y); } + void callListenerOnReleaseKey(final Key releasedKey, final boolean withKeyRegistering) { + releasedKey.onReleased(); + invalidateKey(releasedKey); + if (withKeyRegistering) { + mListener.onReleaseKey(releasedKey); + } + } + + void callListenerOnPressKey(final Key pressedKey) { + mPendingKeyDown = null; + pressedKey.onPressed(); + invalidateKey(pressedKey); + mListener.onPressKey(pressedKey); + } + public void releaseCurrentKey(final boolean withKeyRegistering) { mHandler.removeCallbacks(mPendingKeyDown); mPendingKeyDown = null; @@ -145,11 +160,7 @@ final class EmojiPageKeyboardView extends KeyboardView implements if (currentKey == null) { return; } - currentKey.onReleased(); - invalidateKey(currentKey); - if (withKeyRegistering) { - mListener.onReleaseKey(currentKey); - } + callListenerOnReleaseKey(currentKey, withKeyRegistering); mCurrentKey = null; } @@ -165,10 +176,7 @@ final class EmojiPageKeyboardView extends KeyboardView implements mPendingKeyDown = new Runnable() { @Override public void run() { - mPendingKeyDown = null; - key.onPressed(); - invalidateKey(key); - mListener.onPressKey(key); + callListenerOnPressKey(key); } }; mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME); @@ -195,15 +203,11 @@ final class EmojiPageKeyboardView extends KeyboardView implements mHandler.postDelayed(new Runnable() { @Override public void run() { - key.onReleased(); - invalidateKey(key); - mListener.onReleaseKey(key); + callListenerOnReleaseKey(key, true /* withRegistering */); } }, KEY_RELEASE_DELAY_TIME); } else { - key.onReleased(); - invalidateKey(key); - mListener.onReleaseKey(key); + callListenerOnReleaseKey(key, true /* withRegistering */); } return true; } diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java index e37cd2369..06184f8d2 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.emoji; -import static com.android.inputmethod.latin.Constants.NOT_A_COORDINATE; +import static com.android.inputmethod.latin.common.Constants.NOT_A_COORDINATE; import android.content.Context; import android.content.res.Resources; @@ -47,9 +47,9 @@ import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.ResourceUtils; import java.util.concurrent.TimeUnit; @@ -149,7 +149,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } private void addTab(final TabHost host, final int categoryId) { - final String tabId = mEmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */); + final String tabId = EmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */); final TabHost.TabSpec tspec = host.newTabSpec(tabId); tspec.setContent(R.id.emoji_keyboard_dummy); final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate( diff --git a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java index a194f3dfd..c76a9aca4 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java @@ -19,8 +19,11 @@ package com.android.inputmethod.keyboard.internal; import android.graphics.Canvas; import android.view.View; +import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.PointerTracker; +import javax.annotation.Nonnull; + /** * Abstract base class for previews that are drawn on DrawingPreviewPlacerView, e.g., * GestureFloatingTextDrawingPreview, GestureTrailsDrawingPreview, and @@ -31,7 +34,7 @@ public abstract class AbstractDrawingPreview { private boolean mPreviewEnabled; private boolean mHasValidGeometry; - public void setDrawingView(final DrawingPreviewPlacerView drawingView) { + public void setDrawingView(@Nonnull final DrawingPreviewPlacerView drawingView) { mDrawingView = drawingView; drawingView.addPreview(this); } @@ -51,16 +54,16 @@ public abstract class AbstractDrawingPreview { } /** - * Set {@link MainKeyboardView} geometry and position in the {@link SoftInputWindow}. + * Set {@link MainKeyboardView} geometry and position in the window of input method. * The class that is overriding this method must call this super implementation. * * @param originCoords the top-left coordinates of the {@link MainKeyboardView} in - * {@link SoftInputWindow} coordinate-system. This is unused but has a point in an + * the input method window coordinate-system. This is unused but has a point in an * extended class, such as {@link GestureTrailsDrawingPreview}. * @param width the width of {@link MainKeyboardView}. * @param height the height of {@link MainKeyboardView}. */ - public void setKeyboardViewGeometry(final int[] originCoords, final int width, + public void setKeyboardViewGeometry(@Nonnull final int[] originCoords, final int width, final int height) { mHasValidGeometry = (width > 0 && height > 0); } @@ -71,11 +74,11 @@ public abstract class AbstractDrawingPreview { * Draws the preview * @param canvas The canvas where the preview is drawn. */ - public abstract void drawPreview(final Canvas canvas); + public abstract void drawPreview(@Nonnull final Canvas canvas); /** * Set the position of the preview. * @param tracker The new location of the preview is based on the points in PointerTracker. */ - public abstract void setPreviewPosition(final PointerTracker tracker); + public abstract void setPreviewPosition(@Nonnull final PointerTracker tracker); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java b/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java index cd9875955..77d0e7a90 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java +++ b/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java @@ -16,8 +16,8 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.InputPointers; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; /** * This class arbitrates batch input. diff --git a/java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java b/java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java index 6420edd7a..4b355a4ab 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java +++ b/java/src/com/android/inputmethod/keyboard/internal/BogusMoveEventDetector.java @@ -20,8 +20,8 @@ import android.content.res.Resources; import android.util.DisplayMetrics; import android.util.Log; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.define.DebugFlags; // This hack is applied to certain classes of tablets. diff --git a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java index dce7fc57e..2e2ed52dd 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java @@ -16,8 +16,8 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import android.text.TextUtils; diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java deleted file mode 100644 index 1a55359f5..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.keyboard.internal; - -import android.os.Message; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; - -import javax.annotation.Nonnull; - -// TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so. -public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> { - public interface Callbacks { - public void dismissKeyPreviewWithoutDelay(Key key); - public void dismissAllKeyPreviews(); - public void showGestureFloatingPreviewText(SuggestedWords suggestedWords); - } - - private static final int MSG_DISMISS_KEY_PREVIEW = 0; - private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; - - public DrawingHandler(@Nonnull final Callbacks ownerInstance) { - super(ownerInstance); - } - - @Override - public void handleMessage(final Message msg) { - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { - return; - } - switch (msg.what) { - case MSG_DISMISS_KEY_PREVIEW: - callbacks.dismissKeyPreviewWithoutDelay((Key)msg.obj); - break; - case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: - callbacks.showGestureFloatingPreviewText(SuggestedWords.getEmptyInstance()); - break; - } - } - - public void dismissKeyPreview(final long delay, final Key key) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay); - } - - private void cancelAllDismissKeyPreviews() { - removeMessages(MSG_DISMISS_KEY_PREVIEW); - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { - return; - } - callbacks.dismissAllKeyPreviews(); - } - - public void dismissGestureFloatingPreviewText(final long delay) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay); - } - - public void cancelAllMessages() { - cancelAllDismissKeyPreviews(); - } -} diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java new file mode 100644 index 000000000..7fc586a0f --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java @@ -0,0 +1,72 @@ +/* + * 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.keyboard.internal; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.PointerTracker; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface DrawingProxy { + // TODO: Remove this method. + public void invalidateKey(@Nullable Key key); + + // TODO: Rename this method to onKeyPressed. + public void showKeyPreview(@Nonnull Key key); + + // TODO: Rename this method to onKeyReleased. + public void dismissKeyPreview(@Nonnull Key key); + + /** + * Dismiss a key preview visual without delay. + * @param key the key whose preview visual should be dismissed. + */ + public void dismissKeyPreviewWithoutDelay(@Nonnull Key key); + + // TODO: Rename this method to onKeyLongPressed. + public void onLongPress(@Nonnull PointerTracker tracker); + + /** + * Start a while-typing-animation. + * @param fadeInOrOut {@link #FADE_IN} starts while-typing-fade-in animation. + * {@link #FADE_OUT} starts while-typing-fade-out animation. + */ + public void startWhileTypingAnimation(int fadeInOrOut); + public static final int FADE_IN = 0; + public static final int FADE_OUT = 1; + + /** + * Show sliding-key input preview. + * @param tracker the {@link PointerTracker} that is currently doing the sliding-key input. + * null to dismiss the sliding-key input preview. + */ + public void showSlidingKeyInputPreview(@Nullable PointerTracker tracker); + + /** + * Show gesture trails. + * @param tracker the {@link PointerTracker} whose gesture trail will be shown. + * @param showsFloatingPreviewText when true, a gesture floating preview text will be shown + * with this <code>tracker</code>'s trail. + */ + public void showGestureTrail(@Nonnull PointerTracker tracker, boolean showsFloatingPreviewText); + + /** + * Dismiss a gesture floating preview text without delay. + */ + public void dismissGestureFloatingPreviewTextWithoutDelay(); +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java index 37ea0f17b..330ec52f0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java @@ -29,6 +29,8 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.utils.CoordinateUtils; +import javax.annotation.Nonnull; + /** * The class for single gesture preview text. The class for multiple gesture preview text will be * derived from it. @@ -110,7 +112,11 @@ public class GestureFloatingTextDrawingPreview extends AbstractDrawingPreview { // Nothing to do here. } - public void setSuggetedWords(final SuggestedWords suggestedWords) { + public void dismissGestureFloatingPreviewText() { + setSuggetedWords(SuggestedWords.getEmptyInstance()); + } + + public void setSuggetedWords(@Nonnull final SuggestedWords suggestedWords) { if (!isPreviewEnabled()) { return; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java index 7d09e9d2f..07ef00924 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeDrawingPoints.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.utils.ResizableIntArray; +import com.android.inputmethod.latin.common.ResizableIntArray; /** * This class holds drawing points to represent a gesture stroke on the screen. diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java index e49e538aa..3e901114a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeRecognitionPoints.java @@ -18,9 +18,9 @@ package com.android.inputmethod.keyboard.internal; import android.util.Log; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.InputPointers; -import com.android.inputmethod.latin.utils.ResizableIntArray; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.ResizableIntArray; /** * This class holds event points to recognize a gesture stroke. diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java index bf4c4da10..4d998e443 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailDrawingPoints.java @@ -23,8 +23,8 @@ import android.graphics.Path; import android.graphics.Rect; import android.os.SystemClock; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.ResizableIntArray; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.ResizableIntArray; /** * This class holds drawing points to represent a gesture trail. The gesture trail may contain diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java index 5005b7d7d..d3764877c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java @@ -28,7 +28,6 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.util.ArrayDeque; import java.util.HashMap; -import java.util.HashSet; /** * This class controls pop up key previews. This class decides: @@ -69,12 +68,6 @@ public final class KeyPreviewChoreographer { return mShowingKeyPreviewViews.containsKey(key); } - public void dismissAllKeyPreviews() { - for (final Key key : new HashSet<>(mShowingKeyPreviewViews.keySet())) { - dismissKeyPreview(key, false /* withAnimation */); - } - } - public void dismissKeyPreview(final Key key, final boolean withAnimation) { if (key == null) { return; @@ -148,7 +141,7 @@ public final class KeyPreviewChoreographer { keyPreviewView.setPivotY(previewHeight); } - private void showKeyPreview(final Key key, final KeyPreviewView keyPreviewView, + void showKeyPreview(final Key key, final KeyPreviewView keyPreviewView, final boolean withAnimation) { if (!withAnimation) { keyPreviewView.setVisibility(View.VISIBLE); @@ -166,25 +159,25 @@ public final class KeyPreviewChoreographer { } public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) { - final Animator animator = mParams.createShowUpAnimator(keyPreviewView); - animator.addListener(new AnimatorListenerAdapter() { + final Animator showUpAnimator = mParams.createShowUpAnimator(keyPreviewView); + showUpAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(final Animator animator) { showKeyPreview(key, keyPreviewView, false /* withAnimation */); } }); - return animator; + return showUpAnimator; } private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) { - final Animator animator = mParams.createDismissAnimator(keyPreviewView); - animator.addListener(new AnimatorListenerAdapter() { + final Animator dismissAnimator = mParams.createDismissAnimator(keyPreviewView); + dismissAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(final Animator animator) { dismissKeyPreview(key, false /* withAnimation */); } }); - return animator; + return dismissAnimator; } private static class KeyPreviewAnimators extends AnimatorListenerAdapter { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 48ba8e051..63aab968b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -16,11 +16,11 @@ package com.android.inputmethod.keyboard.internal; -import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT; -import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; +import static com.android.inputmethod.latin.common.Constants.CODE_OUTPUT_TEXT; +import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; /** * The string parser of the key specification. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index f4e010c4d..c739bf3e0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -32,10 +32,10 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardTheme; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.ResourceUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.XmlParseUtils; import com.android.inputmethod.latin.utils.XmlParseUtils.ParseException; @@ -47,6 +47,8 @@ import java.io.IOException; import java.util.Arrays; import java.util.Locale; +import javax.annotation.Nonnull; + /** * Keyboard Building helper. * @@ -733,18 +735,18 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } } - private boolean matchLocaleCodes(TypedArray caseAttr, final Locale[] locales) { + private static boolean matchLocaleCodes(TypedArray caseAttr, final Locale[] locales) { // TODO: adujst this for multilingual input return matchString(caseAttr, R.styleable.Keyboard_Case_localeCode, locales[0].toString()); } - private boolean matchLanguageCodes(TypedArray caseAttr, Locale[] locales) { + private static boolean matchLanguageCodes(TypedArray caseAttr, Locale[] locales) { // TODO: adujst this for multilingual input return matchString(caseAttr, R.styleable.Keyboard_Case_languageCode, locales[0].getLanguage()); } - private boolean matchCountryCodes(TypedArray caseAttr, Locale[] locales) { + private static boolean matchCountryCodes(TypedArray caseAttr, Locale[] locales) { // TODO: adujst this for multilingual input return matchString(caseAttr, R.styleable.Keyboard_Case_countryCode, locales[0].getCountry()); @@ -859,7 +861,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { mTopEdge = false; } - private void endKey(final Key key) { + private void endKey(@Nonnull final Key key) { mParams.onAddKey(key); if (mLeftEdge) { key.markAsLeftEdge(mParams); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index 62b69dcc9..05b4c7473 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.HashMap; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java index 71ce768a9..fb5e97757 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java @@ -20,7 +20,7 @@ import android.util.SparseIntArray; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.ArrayList; import java.util.Comparator; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 5f4d55bdb..70e116709 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -20,7 +20,8 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.event.Event; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.RecapitalizeStatus; /** @@ -38,9 +39,11 @@ import com.android.inputmethod.latin.utils.RecapitalizeStatus; public final class KeyboardState { private static final String TAG = KeyboardState.class.getSimpleName(); private static final boolean DEBUG_EVENT = false; - private static final boolean DEBUG_ACTION = false; + private static final boolean DEBUG_INTERNAL_ACTION = false; public interface SwitchActions { + public static final boolean DEBUG_ACTION = false; + public void setAlphabetKeyboard(); public void setAlphabetManualShiftedKeyboard(); public void setAlphabetAutomaticShiftedKeyboard(); @@ -53,8 +56,9 @@ public final class KeyboardState { /** * Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}. */ - public void requestUpdatingShiftState(final int currentAutoCapsState, - final int currentRecapitalizeState); + public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode); + + public static final boolean DEBUG_TIMER_ACTION = false; public void startDoubleTapShiftKeyTimer(); public boolean isInDoubleTapShiftKeyTimeout(); @@ -119,10 +123,9 @@ public final class KeyboardState { mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; } - public void onLoadKeyboard(final int currentAutoCapsState, - final int currentRecapitalizeState) { + public void onLoadKeyboard(final int autoCapsFlags, final int recapitalizeMode) { if (DEBUG_EVENT) { - Log.d(TAG, "onLoadKeyboard: " + this); + Log.d(TAG, "onLoadKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode)); } // Reset alphabet shift state. mAlphabetShiftState.setShiftLocked(false); @@ -130,7 +133,7 @@ public final class KeyboardState { mPrevSymbolsKeyboardWasShifted = false; mShiftKeyState.onRelease(); mSymbolKeyState.onRelease(); - onRestoreKeyboardState(currentAutoCapsState, currentRecapitalizeState); + onRestoreKeyboardState(autoCapsFlags, recapitalizeMode); } private static final int UNSHIFT = 0; @@ -156,14 +159,14 @@ public final class KeyboardState { } } - private void onRestoreKeyboardState(final int currentAutoCapsState, - final int currentRecapitalizeState) { + private void onRestoreKeyboardState(final int autoCapsFlags, final int recapitalizeMode) { final SavedKeyboardState state = mSavedKeyboardState; if (DEBUG_EVENT) { - Log.d(TAG, "onRestoreKeyboardState: saved=" + state + " " + this); + Log.d(TAG, "onRestoreKeyboardState: saved=" + state + + " " + stateToString(autoCapsFlags, recapitalizeMode)); } if (!state.mIsValid || state.mIsAlphabetMode) { - setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState); + setAlphabetKeyboard(autoCapsFlags, recapitalizeMode); } else if (state.mIsEmojiMode) { setEmojiKeyboard(); } else { @@ -188,7 +191,7 @@ public final class KeyboardState { } private void setShifted(final int shiftMode) { - if (DEBUG_ACTION) { + if (DEBUG_INTERNAL_ACTION) { Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode) + " " + this); } if (!mIsAlphabetMode) return; @@ -227,7 +230,7 @@ public final class KeyboardState { } private void setShiftLocked(final boolean shiftLocked) { - if (DEBUG_ACTION) { + if (DEBUG_INTERNAL_ACTION) { Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked + " " + this); } if (!mIsAlphabetMode) return; @@ -241,10 +244,10 @@ public final class KeyboardState { mAlphabetShiftState.setShiftLocked(shiftLocked); } - private void toggleAlphabetAndSymbols(final int currentAutoCapsState, - final int currentRecapitalizeState) { - if (DEBUG_ACTION) { - Log.d(TAG, "toggleAlphabetAndSymbols: " + this); + private void toggleAlphabetAndSymbols(final int autoCapsFlags, final int recapitalizeMode) { + if (DEBUG_INTERNAL_ACTION) { + Log.d(TAG, "toggleAlphabetAndSymbols: " + + stateToString(autoCapsFlags, recapitalizeMode)); } if (mIsAlphabetMode) { mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked(); @@ -256,7 +259,7 @@ public final class KeyboardState { mPrevSymbolsKeyboardWasShifted = false; } else { mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; - setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState); + setAlphabetKeyboard(autoCapsFlags, recapitalizeMode); if (mPrevMainKeyboardWasShiftLocked) { setShiftLocked(true); } @@ -266,15 +269,15 @@ public final class KeyboardState { // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). - private void resetKeyboardStateToAlphabet(final int currentAutoCapsState, - final int currentRecapitalizeState) { - if (DEBUG_ACTION) { - Log.d(TAG, "resetKeyboardStateToAlphabet: " + this); + private void resetKeyboardStateToAlphabet(final int autoCapsFlags, final int recapitalizeMode) { + if (DEBUG_INTERNAL_ACTION) { + Log.d(TAG, "resetKeyboardStateToAlphabet: " + + stateToString(autoCapsFlags, recapitalizeMode)); } if (mIsAlphabetMode) return; mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; - setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState); + setAlphabetKeyboard(autoCapsFlags, recapitalizeMode); if (mPrevMainKeyboardWasShiftLocked) { setShiftLocked(true); } @@ -289,10 +292,9 @@ public final class KeyboardState { } } - private void setAlphabetKeyboard(final int currentAutoCapsState, - final int currentRecapitalizeState) { - if (DEBUG_ACTION) { - Log.d(TAG, "setAlphabetKeyboard"); + private void setAlphabetKeyboard(final int autoCapsFlags, final int recapitalizeMode) { + if (DEBUG_INTERNAL_ACTION) { + Log.d(TAG, "setAlphabetKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode)); } mSwitchActions.setAlphabetKeyboard(); @@ -301,11 +303,11 @@ public final class KeyboardState { mIsSymbolShifted = false; mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; mSwitchState = SWITCH_STATE_ALPHA; - mSwitchActions.requestUpdatingShiftState(currentAutoCapsState, currentRecapitalizeState); + mSwitchActions.requestUpdatingShiftState(autoCapsFlags, recapitalizeMode); } private void setSymbolsKeyboard() { - if (DEBUG_ACTION) { + if (DEBUG_INTERNAL_ACTION) { Log.d(TAG, "setSymbolsKeyboard"); } mSwitchActions.setSymbolsKeyboard(); @@ -318,7 +320,7 @@ public final class KeyboardState { } private void setSymbolsShiftedKeyboard() { - if (DEBUG_ACTION) { + if (DEBUG_INTERNAL_ACTION) { Log.d(TAG, "setSymbolsShiftedKeyboard"); } mSwitchActions.setSymbolsShiftedKeyboard(); @@ -331,7 +333,7 @@ public final class KeyboardState { } private void setEmojiKeyboard() { - if (DEBUG_ACTION) { + if (DEBUG_INTERNAL_ACTION) { Log.d(TAG, "setEmojiKeyboard"); } mIsAlphabetMode = false; @@ -343,11 +345,12 @@ public final class KeyboardState { mSwitchActions.setEmojiKeyboard(); } - public void onPressKey(final int code, final boolean isSinglePointer, - final int currentAutoCapsState, final int currentRecapitalizeState) { + public void onPressKey(final int code, final boolean isSinglePointer, final int autoCapsFlags, + final int recapitalizeMode) { if (DEBUG_EVENT) { - Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code) + " single=" - + isSinglePointer + " autoCaps=" + currentAutoCapsState + " " + this); + Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code) + + " single=" + isSinglePointer + + " " + stateToString(autoCapsFlags, recapitalizeMode)); } if (code != Constants.CODE_SHIFT) { // Because the double tap shift key timer is to detect two consecutive shift key press, @@ -359,7 +362,7 @@ public final class KeyboardState { } else if (code == Constants.CODE_CAPSLOCK) { // Nothing to do here. See {@link #onReleaseKey(int,boolean)}. } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { - onPressSymbol(currentAutoCapsState, currentRecapitalizeState); + onPressSymbol(autoCapsFlags, recapitalizeMode); } else { mShiftKeyState.onOtherKeyPressed(); mSymbolKeyState.onOtherKeyPressed(); @@ -372,7 +375,7 @@ public final class KeyboardState { // off because, for example, we may be in the #1 state within the manual temporary // shifted mode. if (!isSinglePointer && mIsAlphabetMode - && currentAutoCapsState != TextUtils.CAP_MODE_CHARACTERS) { + && autoCapsFlags != TextUtils.CAP_MODE_CHARACTERS) { final boolean needsToResetAutoCaps = mAlphabetShiftState.isAutomaticShifted() || (mAlphabetShiftState.isManualShifted() && mShiftKeyState.isReleasing()); if (needsToResetAutoCaps) { @@ -382,34 +385,35 @@ public final class KeyboardState { } } - public void onReleaseKey(final int code, final boolean withSliding, - final int currentAutoCapsState, final int currentRecapitalizeState) { + public void onReleaseKey(final int code, final boolean withSliding, final int autoCapsFlags, + final int recapitalizeMode) { if (DEBUG_EVENT) { Log.d(TAG, "onReleaseKey: code=" + Constants.printableCode(code) - + " sliding=" + withSliding + " " + this); + + " sliding=" + withSliding + + " " + stateToString(autoCapsFlags, recapitalizeMode)); } if (code == Constants.CODE_SHIFT) { - onReleaseShift(withSliding, currentAutoCapsState, currentRecapitalizeState); + onReleaseShift(withSliding, autoCapsFlags, recapitalizeMode); } else if (code == Constants.CODE_CAPSLOCK) { setShiftLocked(!mAlphabetShiftState.isShiftLocked()); } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { - onReleaseSymbol(withSliding, currentAutoCapsState, currentRecapitalizeState); + onReleaseSymbol(withSliding, autoCapsFlags, recapitalizeMode); } } - private void onPressSymbol(final int currentAutoCapsState, - final int currentRecapitalizeState) { - toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState); + private void onPressSymbol(final int autoCapsFlags, + final int recapitalizeMode) { + toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode); mSymbolKeyState.onPress(); mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL; } - private void onReleaseSymbol(final boolean withSliding, final int currentAutoCapsState, - final int currentRecapitalizeState) { + private void onReleaseSymbol(final boolean withSliding, final int autoCapsFlags, + final int recapitalizeMode) { if (mSymbolKeyState.isChording()) { // Switch back to the previous keyboard mode if the user chords the mode change key and // another key, then releases the mode change key. - toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState); + toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode); } else if (!withSliding) { // If the mode change key is being released without sliding, we should forget the // previous symbols keyboard shift state and simply switch back to symbols layout @@ -419,23 +423,23 @@ public final class KeyboardState { mSymbolKeyState.onRelease(); } - public void onUpdateShiftState(final int autoCaps, final int recapitalizeMode) { + public void onUpdateShiftState(final int autoCapsFlags, final int recapitalizeMode) { if (DEBUG_EVENT) { - Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + ", recapitalizeMode=" - + recapitalizeMode + " " + this); + Log.d(TAG, "onUpdateShiftState: " + stateToString(autoCapsFlags, recapitalizeMode)); } mRecapitalizeMode = recapitalizeMode; - updateAlphabetShiftState(autoCaps, recapitalizeMode); + updateAlphabetShiftState(autoCapsFlags, recapitalizeMode); } // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). - public void onResetKeyboardStateToAlphabet(final int currentAutoCapsState, - final int currentRecapitalizeState) { + public void onResetKeyboardStateToAlphabet(final int autoCapsFlags, + final int recapitalizeMode) { if (DEBUG_EVENT) { - Log.d(TAG, "onResetKeyboardStateToAlphabet: " + this); + Log.d(TAG, "onResetKeyboardStateToAlphabet: " + + stateToString(autoCapsFlags, recapitalizeMode)); } - resetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState); + resetKeyboardStateToAlphabet(autoCapsFlags, recapitalizeMode); } private void updateShiftStateForRecapitalize(final int recapitalizeMode) { @@ -453,7 +457,7 @@ public final class KeyboardState { } } - private void updateAlphabetShiftState(final int autoCaps, final int recapitalizeMode) { + private void updateAlphabetShiftState(final int autoCapsFlags, final int recapitalizeMode) { if (!mIsAlphabetMode) return; if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != recapitalizeMode) { // We are recapitalizing. Match the keyboard to the current recapitalize state. @@ -466,7 +470,7 @@ public final class KeyboardState { return; } if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) { - if (mShiftKeyState.isReleasing() && autoCaps != Constants.TextUtils.CAP_MODE_OFF) { + if (mShiftKeyState.isReleasing() && autoCapsFlags != Constants.TextUtils.CAP_MODE_OFF) { // Only when shift key is releasing, automatic temporary upper case will be set. setShifted(AUTOMATIC_SHIFT); } else { @@ -526,8 +530,8 @@ public final class KeyboardState { } } - private void onReleaseShift(final boolean withSliding, final int currentAutoCapsState, - final int currentRecapitalizeState) { + private void onReleaseShift(final boolean withSliding, final int autoCapsFlags, + final int recapitalizeMode) { if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { // We are recapitalizing. We should match the keyboard state to the recapitalize // state in priority. @@ -550,8 +554,7 @@ public final class KeyboardState { // After chording input, automatic shift state may have been changed depending on // what characters were input. mShiftKeyState.onRelease(); - mSwitchActions.requestUpdatingShiftState(currentAutoCapsState, - currentRecapitalizeState); + mSwitchActions.requestUpdatingShiftState(autoCapsFlags, recapitalizeMode); return; } else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) { // In shift locked state, shift has been pressed and slid out to other key. @@ -588,21 +591,20 @@ public final class KeyboardState { mShiftKeyState.onRelease(); } - public void onFinishSlidingInput(final int currentAutoCapsState, - final int currentRecapitalizeState) { + public void onFinishSlidingInput(final int autoCapsFlags, final int recapitalizeMode) { if (DEBUG_EVENT) { - Log.d(TAG, "onFinishSlidingInput: " + this); + Log.d(TAG, "onFinishSlidingInput: " + stateToString(autoCapsFlags, recapitalizeMode)); } // Switch back to the previous keyboard mode if the user cancels sliding input. switch (mSwitchState) { case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: - toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState); + toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode); break; case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: toggleShiftInSymbols(); break; case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: - setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState); + setAlphabetKeyboard(autoCapsFlags, recapitalizeMode); break; } } @@ -611,12 +613,11 @@ public final class KeyboardState { return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER; } - public void onEvent(final Event event, final int currentAutoCapsState, - final int currentRecapitalizeState) { + public void onEvent(final Event event, final int autoCapsFlags, final int recapitalizeMode) { final int code = event.isFunctionalKeyEvent() ? event.mKeyCode : event.mCodePoint; if (DEBUG_EVENT) { Log.d(TAG, "onEvent: code=" + Constants.printableCode(code) - + " autoCaps=" + currentAutoCapsState + " " + this); + + " " + stateToString(autoCapsFlags, recapitalizeMode)); } switch (mSwitchState) { @@ -652,7 +653,7 @@ public final class KeyboardState { // Switch back to alpha keyboard mode if user types one or more non-space/enter // characters followed by a space/enter. if (isSpaceOrEnter(code)) { - toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState); + toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode); mPrevSymbolsKeyboardWasShifted = false; } break; @@ -660,11 +661,11 @@ public final class KeyboardState { // If the code is a letter, update keyboard shift state. if (Constants.isLetterCode(code)) { - updateAlphabetShiftState(currentAutoCapsState, currentRecapitalizeState); + updateAlphabetShiftState(autoCapsFlags, recapitalizeMode); } else if (code == Constants.CODE_EMOJI) { setEmojiKeyboard(); } else if (code == Constants.CODE_ALPHA_FROM_EMOJI) { - setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState); + setAlphabetKeyboard(autoCapsFlags, recapitalizeMode); } } @@ -697,4 +698,9 @@ public final class KeyboardState { + " symbol=" + mSymbolKeyState + " switch=" + switchStateToString(mSwitchState) + "]"; } + + private String stateToString(final int autoCapsFlags, final int recapitalizeMode) { + return this + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags) + + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode); + } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index f9297ac27..0aaf6b401 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -21,7 +21,7 @@ import android.content.res.Resources; import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; diff --git a/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java b/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java index c1f374964..d927cc362 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java @@ -28,7 +28,8 @@ import java.util.Arrays; */ @UsedForTesting public class MatrixUtils { - private static final String TAG = MatrixUtils.class.getSimpleName(); + static final String TAG = MatrixUtils.class.getSimpleName(); + public static class MatrixOperationFailedException extends Exception { private static final long serialVersionUID = 4384485606788583829L; diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java index 0cd031e5f..a0bb406aa 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java @@ -21,16 +21,18 @@ import android.util.SparseIntArray; import com.android.inputmethod.compat.CharacterCompat; import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Locale; +import javax.annotation.Nonnull; + /** * The more key specification object. The more keys are an array of {@link MoreKeySpec}. * @@ -70,6 +72,7 @@ public final class MoreKeySpec { mIconId = KeySpecParser.getIconId(moreKeySpec); } + @Nonnull public Key buildKey(final int x, final int y, final int labelFlags, final KeyboardParams params) { return new Key(mLabel, mIconId, mCode, mOutputText, null /* hintLabel */, labelFlags, @@ -108,9 +111,8 @@ public final class MoreKeySpec { : Constants.printableCode(mCode)); if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) { return output; - } else { - return label + "|" + output; } + return label + "|" + output; } public static class LettersOnBaseLayout { diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java index 80b299bf5..8068427bc 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java @@ -22,33 +22,27 @@ import android.view.ViewConfiguration; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.PointerTracker; -import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; -import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import javax.annotation.Nonnull; -// TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so. -public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy { - public interface Callbacks { - public void startWhileTypingFadeinAnimation(); - public void startWhileTypingFadeoutAnimation(); - public void onLongPress(PointerTracker tracker); - } - +public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy> + implements TimerProxy { private static final int MSG_TYPING_STATE_EXPIRED = 0; private static final int MSG_REPEAT_KEY = 1; private static final int MSG_LONGPRESS_KEY = 2; private static final int MSG_LONGPRESS_SHIFT_KEY = 3; private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 4; private static final int MSG_UPDATE_BATCH_INPUT = 5; + private static final int MSG_DISMISS_KEY_PREVIEW = 6; + private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 7; private final int mIgnoreAltCodeKeyTimeout; private final int mGestureRecognitionUpdateTime; - public TimerHandler(@Nonnull final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, - final int gestureRecognitionUpdateTime) { + public TimerHandler(@Nonnull final DrawingProxy ownerInstance, + final int ignoreAltCodeKeyTimeout, final int gestureRecognitionUpdateTime) { super(ownerInstance); mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout; mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime; @@ -56,32 +50,41 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple @Override public void handleMessage(final Message msg) { - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { + final DrawingProxy drawingProxy = getOwnerInstance(); + if (drawingProxy == null) { return; } - final PointerTracker tracker = (PointerTracker) msg.obj; switch (msg.what) { case MSG_TYPING_STATE_EXPIRED: - callbacks.startWhileTypingFadeinAnimation(); + drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN); break; case MSG_REPEAT_KEY: - tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */); + final PointerTracker tracker1 = (PointerTracker) msg.obj; + tracker1.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */); break; case MSG_LONGPRESS_KEY: case MSG_LONGPRESS_SHIFT_KEY: cancelLongPressTimers(); - callbacks.onLongPress(tracker); + final PointerTracker tracker2 = (PointerTracker) msg.obj; + drawingProxy.onLongPress(tracker2); break; case MSG_UPDATE_BATCH_INPUT: - tracker.updateBatchInputByTimer(SystemClock.uptimeMillis()); - startUpdateBatchInputTimer(tracker); + final PointerTracker tracker3 = (PointerTracker) msg.obj; + tracker3.updateBatchInputByTimer(SystemClock.uptimeMillis()); + startUpdateBatchInputTimer(tracker3); + break; + case MSG_DISMISS_KEY_PREVIEW: + final Key key = (Key) msg.obj; + drawingProxy.dismissKeyPreviewWithoutDelay(key); + break; + case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: + drawingProxy.dismissGestureFloatingPreviewTextWithoutDelay(); break; } } @Override - public void startKeyRepeatTimerOf(final PointerTracker tracker, final int repeatCount, + public void startKeyRepeatTimerOf(@Nonnull final PointerTracker tracker, final int repeatCount, final int delay) { final Key key = tracker.getKey(); if (key == null || delay == 0) { @@ -105,7 +108,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple } @Override - public void startLongPressTimerOf(final PointerTracker tracker, final int delay) { + public void startLongPressTimerOf(@Nonnull final PointerTracker tracker, final int delay) { final Key key = tracker.getKey(); if (key == null) { return; @@ -118,13 +121,13 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple } @Override - public void cancelLongPressTimerOf(final PointerTracker tracker) { + public void cancelLongPressTimersOf(@Nonnull final PointerTracker tracker) { removeMessages(MSG_LONGPRESS_KEY, tracker); removeMessages(MSG_LONGPRESS_SHIFT_KEY, tracker); } @Override - public void cancelLongPressShiftKeyTimers() { + public void cancelLongPressShiftKeyTimer() { removeMessages(MSG_LONGPRESS_SHIFT_KEY); } @@ -134,15 +137,15 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple } @Override - public void startTypingStateTimer(final Key typedKey) { + public void startTypingStateTimer(@Nonnull final Key typedKey) { if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) { return; } final boolean isTyping = isTypingState(); removeMessages(MSG_TYPING_STATE_EXPIRED); - final Callbacks callbacks = getOwnerInstance(); - if (callbacks == null) { + final DrawingProxy drawingProxy = getOwnerInstance(); + if (drawingProxy == null) { return; } @@ -150,7 +153,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple final int typedCode = typedKey.getCode(); if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) { if (isTyping) { - callbacks.startWhileTypingFadeinAnimation(); + drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_IN); } return; } @@ -160,7 +163,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple if (isTyping) { return; } - callbacks.startWhileTypingFadeoutAnimation(); + drawingProxy.startWhileTypingAnimation(DrawingProxy.FADE_OUT); } @Override @@ -185,9 +188,9 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple } @Override - public void cancelKeyTimersOf(final PointerTracker tracker) { + public void cancelKeyTimersOf(@Nonnull final PointerTracker tracker) { cancelKeyRepeatTimerOf(tracker); - cancelLongPressTimerOf(tracker); + cancelLongPressTimersOf(tracker); } public void cancelAllKeyTimers() { @@ -196,7 +199,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple } @Override - public void startUpdateBatchInputTimer(final PointerTracker tracker) { + public void startUpdateBatchInputTimer(@Nonnull final PointerTracker tracker) { if (mGestureRecognitionUpdateTime <= 0) { return; } @@ -206,7 +209,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple } @Override - public void cancelUpdateBatchInputTimer(final PointerTracker tracker) { + public void cancelUpdateBatchInputTimer(@Nonnull final PointerTracker tracker) { removeMessages(MSG_UPDATE_BATCH_INPUT, tracker); } @@ -215,8 +218,18 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple removeMessages(MSG_UPDATE_BATCH_INPUT); } + public void postDismissKeyPreview(@Nonnull final Key key, final long delay) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay); + } + + public void postDismissGestureFloatingPreviewText(final long delay) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay); + } + public void cancelAllMessages() { cancelAllKeyTimers(); cancelAllUpdateBatchInputTimers(); + removeMessages(MSG_DISMISS_KEY_PREVIEW); + removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java b/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java new file mode 100644 index 000000000..0ce3de8d9 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java @@ -0,0 +1,133 @@ +/* + * 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.keyboard.internal; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.PointerTracker; + +import javax.annotation.Nonnull; + +public interface TimerProxy { + /** + * Start a timer to detect if a user is typing keys. + * @param typedKey the key that is typed. + */ + public void startTypingStateTimer(@Nonnull Key typedKey); + + /** + * Check if a user is key typing. + * @return true if a user is in typing. + */ + public boolean isTypingState(); + + /** + * Start a timer to simulate repeated key presses while a user keep pressing a key. + * @param tracker the {@link PointerTracker} that points the key to be repeated. + * @param repeatCount the number of times that the key is repeating. Starting from 1. + * @param delay the interval delay to the next key repeat, in millisecond. + */ + public void startKeyRepeatTimerOf(@Nonnull PointerTracker tracker, int repeatCount, int delay); + + /** + * Start a timer to detect a long pressed key. + * If a key pointed by <code>tracker</code> is a shift key, start another timer to detect + * long pressed shift key. + * @param tracker the {@link PointerTracker} that starts long pressing. + * @param delay the delay to fire the long press timer, in millisecond. + */ + public void startLongPressTimerOf(@Nonnull PointerTracker tracker, int delay); + + /** + * Cancel timers for detecting a long pressed key and a long press shift key. + * @param tracker cancel long press timers of this {@link PointerTracker}. + */ + public void cancelLongPressTimersOf(@Nonnull PointerTracker tracker); + + /** + * Cancel a timer for detecting a long pressed shift key. + */ + public void cancelLongPressShiftKeyTimer(); + + /** + * Cancel timers for detecting repeated key press, long pressed key, and long pressed shift key. + * @param tracker the {@link PointerTracker} that starts timers to be canceled. + */ + public void cancelKeyTimersOf(@Nonnull PointerTracker tracker); + + /** + * Start a timer to detect double tapped shift key. + */ + public void startDoubleTapShiftKeyTimer(); + + /** + * Cancel a timer of detecting double tapped shift key. + */ + public void cancelDoubleTapShiftKeyTimer(); + + /** + * Check if a timer of detecting double tapped shift key is running. + * @return true if detecting double tapped shift key is on going. + */ + public boolean isInDoubleTapShiftKeyTimeout(); + + /** + * Start a timer to fire updating batch input while <code>tracker</code> is on hold. + * @param tracker the {@link PointerTracker} that stops moving. + */ + public void startUpdateBatchInputTimer(@Nonnull PointerTracker tracker); + + /** + * Cancel a timer of firing updating batch input. + * @param tracker the {@link PointerTracker} that resumes moving or ends gesture input. + */ + public void cancelUpdateBatchInputTimer(@Nonnull PointerTracker tracker); + + /** + * Cancel all timers of firing updating batch input. + */ + public void cancelAllUpdateBatchInputTimers(); + + public static class Adapter implements TimerProxy { + @Override + public void startTypingStateTimer(@Nonnull Key typedKey) {} + @Override + public boolean isTypingState() { return false; } + @Override + public void startKeyRepeatTimerOf(@Nonnull PointerTracker tracker, int repeatCount, + int delay) {} + @Override + public void startLongPressTimerOf(@Nonnull PointerTracker tracker, int delay) {} + @Override + public void cancelLongPressTimersOf(@Nonnull PointerTracker tracker) {} + @Override + public void cancelLongPressShiftKeyTimer() {} + @Override + public void cancelKeyTimersOf(@Nonnull PointerTracker tracker) {} + @Override + public void startDoubleTapShiftKeyTimer() {} + @Override + public void cancelDoubleTapShiftKeyTimer() {} + @Override + public boolean isInDoubleTapShiftKeyTimeout() { return false; } + @Override + public void startUpdateBatchInputTimer(@Nonnull PointerTracker tracker) {} + @Override + public void cancelUpdateBatchInputTimer(@Nonnull PointerTracker tracker) {} + @Override + public void cancelAllUpdateBatchInputTimers() {} + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java index fef97cc11..d8f0114e1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java @@ -80,6 +80,7 @@ public final class TouchPositionCorrection { return mRadii.length; } + @SuppressWarnings({ "static-method", "unused" }) public float getX(final int row) { return 0.0f; // Touch position correction data for X coordinate is obsolete. diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java index eb8b34ccd..60d257362 100644 --- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java +++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java @@ -22,6 +22,7 @@ import android.os.Vibrator; import android.view.HapticFeedbackConstants; import android.view.View; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.SettingsValues; /** diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 2fece7c85..b5d0b446f 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -21,8 +21,11 @@ import android.util.Log; import android.util.SparseArray; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; @@ -32,8 +35,7 @@ import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.FileUtils; import com.android.inputmethod.latin.utils.JniUtils; -import com.android.inputmethod.latin.utils.LanguageModelParam; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; import java.io.File; import java.util.ArrayList; @@ -69,7 +71,7 @@ public final class BinaryDictionary extends Dictionary { // Format to get unigram flags from native side via getWordPropertyNative(). private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 5; private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0; - private static final int FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX = 1; + private static final int FORMAT_WORD_PROPERTY_IS_POSSIBLY_OFFENSIVE_INDEX = 1; private static final int FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX = 2; private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3; private static final int FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX = 4; @@ -195,7 +197,7 @@ public final class BinaryDictionary extends Dictionary { float[] inOutWeightOfLangModelVsSpatialModel); private static native boolean addUnigramEntryNative(long dict, int[] word, int probability, int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence, - boolean isNotAWord, boolean isBlacklisted, int timestamp); + boolean isNotAWord, boolean isPossiblyOffensive, int timestamp); private static native boolean removeUnigramEntryNative(long dict, int[] word); private static native boolean addNgramEntryNative(long dict, int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, @@ -205,8 +207,8 @@ public final class BinaryDictionary extends Dictionary { private static native boolean updateEntriesForWordWithNgramContextNative(long dict, int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word, boolean isValidWord, int count, int timestamp); - private static native int addMultipleDictionaryEntriesNative(long dict, - LanguageModelParam[] languageModelParams, int startIndex); + private static native int updateEntriesForInputEventsNative(long dict, + WordInputEventForPersonalization[] inputEvents, int startIndex); private static native String getPropertyNative(long dict, String query); private static native boolean isCorruptedNative(long dict); private static native boolean migrateNative(long dict, String dictFilePath, @@ -260,8 +262,8 @@ public final class BinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { @@ -272,12 +274,13 @@ public final class BinaryDictionary extends Dictionary { Arrays.fill(session.mInputCodePoints, Constants.NOT_A_CODE); ngramContext.outputToArray(session.mPrevWordCodePointArrays, session.mIsBeginningOfSentenceArray); - final InputPointers inputPointers = composer.getInputPointers(); - final boolean isGesture = composer.isBatchMode(); + final InputPointers inputPointers = composedData.mInputPointers; + final boolean isGesture = composedData.mIsBatchMode; final int inputSize; if (!isGesture) { - inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( - session.mInputCodePoints); + inputSize = + composedData.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( + session.mInputCodePoints); if (inputSize < 0) { return null; } @@ -301,7 +304,7 @@ public final class BinaryDictionary extends Dictionary { Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL; } // TOOD: Pass multiple previous words information for n-gram. - getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), + getSuggestionsNative(mNativeDict, proximityInfoHandle, getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(), inputPointers.getYCoordinates(), inputPointers.getTimes(), inputPointers.getPointerIds(), session.mInputCodePoints, inputSize, @@ -351,15 +354,19 @@ public final class BinaryDictionary extends Dictionary { @Override public int getFrequency(final String word) { - if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; - int[] codePoints = StringUtils.toCodePointArray(word); + if (TextUtils.isEmpty(word)) { + return NOT_A_PROBABILITY; + } + final int[] codePoints = StringUtils.toCodePointArray(word); return getProbabilityNative(mNativeDict, codePoints); } @Override public int getMaxFrequencyOfExactMatches(final String word) { - if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; - int[] codePoints = StringUtils.toCodePointArray(word); + if (TextUtils.isEmpty(word)) { + return NOT_A_PROBABILITY; + } + final int[] codePoints = StringUtils.toCodePointArray(word); return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints); } @@ -402,7 +409,7 @@ public final class BinaryDictionary extends Dictionary { outNgramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); return new WordProperty(codePoints, outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX], - outFlags[FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX], + outFlags[FORMAT_WORD_PROPERTY_IS_POSSIBLY_OFFENSIVE_INDEX], outFlags[FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX], outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], outFlags[FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX], outProbabilityInfo, @@ -439,7 +446,7 @@ public final class BinaryDictionary extends Dictionary { public boolean addUnigramEntry(final String word, final int probability, final String shortcutTarget, final int shortcutProbability, final boolean isBeginningOfSentence, final boolean isNotAWord, - final boolean isBlacklisted, final int timestamp) { + final boolean isPossiblyOffensive, final int timestamp) { if (word == null || (word.isEmpty() && !isBeginningOfSentence)) { return false; } @@ -447,7 +454,8 @@ public final class BinaryDictionary extends Dictionary { final int[] shortcutTargetCodePoints = (shortcutTarget != null) ? StringUtils.toCodePointArray(shortcutTarget) : null; if (!addUnigramEntryNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, - shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) { + shortcutProbability, isBeginningOfSentence, isNotAWord, isPossiblyOffensive, + timestamp)) { return false; } mHasUpdated = true; @@ -521,17 +529,19 @@ public final class BinaryDictionary extends Dictionary { } @UsedForTesting - public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) { - if (!isValidDictionary()) return; - int processedParamCount = 0; - while (processedParamCount < languageModelParams.length) { + public void updateEntriesForInputEvents(final WordInputEventForPersonalization[] inputEvents) { + if (!isValidDictionary()) { + return; + } + int processedEventCount = 0; + while (processedEventCount < inputEvents.length) { if (needsToRunGC(true /* mindsBlockByGC */)) { flushWithGC(); } - processedParamCount = addMultipleDictionaryEntriesNative(mNativeDict, - languageModelParams, processedParamCount); + processedEventCount = updateEntriesForInputEventsNative(mNativeDict, inputEvents, + processedEventCount); mHasUpdated = true; - if (processedParamCount <= 0) { + if (processedEventCount <= 0) { return; } } @@ -549,7 +559,9 @@ public final class BinaryDictionary extends Dictionary { // Flush to dict file if the dictionary has been updated. public boolean flush() { - if (!isValidDictionary()) return false; + if (!isValidDictionary()) { + return false; + } if (mHasUpdated) { if (!flushNative(mNativeDict, mDictFilePath)) { return false; @@ -569,7 +581,9 @@ public final class BinaryDictionary extends Dictionary { // Run GC and flush to dict file. public boolean flushWithGC() { - if (!isValidDictionary()) return false; + if (!isValidDictionary()) { + return false; + } if (!flushWithGCNative(mNativeDict, mDictFilePath)) { return false; } @@ -584,7 +598,9 @@ public final class BinaryDictionary extends Dictionary { * @return whether GC is needed to run or not. */ public boolean needsToRunGC(final boolean mindsBlockByGC) { - if (!isValidDictionary()) return false; + if (!isValidDictionary()) { + return false; + } return needsToRunGCNative(mNativeDict, mindsBlockByGC); } @@ -629,7 +645,9 @@ public final class BinaryDictionary extends Dictionary { @UsedForTesting public String getPropertyForGettingStats(final String query) { - if (!isValidDictionary()) return ""; + if (!isValidDictionary()) { + return ""; + } return getPropertyNative(mNativeDict, query); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 867c18686..1570bdae0 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -121,12 +121,11 @@ final public class BinaryDictionaryGetter { // reason some dictionaries have been installed BUT the dictionary pack can't be // found anymore it's safer to actually supply installed dictionaries. return true; - } else { - // The default is true here for the same reasons as above. We got the dictionary - // pack but if we don't have any settings for it it means the user has never been - // to the settings yet. So by default, the main dictionaries should be on. - return mDictPreferences.getBoolean(dictId, true); } + // The default is true here for the same reasons as above. We got the dictionary + // pack but if we don't have any settings for it it means the user has never been + // to the settings yet. So by default, the main dictionaries should be on. + return mDictPreferences.getBoolean(dictId, true); } } @@ -224,7 +223,7 @@ final public class BinaryDictionaryGetter { // ## HACK ## we prevent usage of a dictionary before version 18. The reason for this is, since // those do not include whitelist entries, the new code with an old version of the dictionary // would lose whitelist functionality. - private static boolean hackCanUseDictionaryFile(final Locale locale, final File file) { + private static boolean hackCanUseDictionaryFile(final File file) { try { // Read the version of the file final DictionaryHeader header = BinaryDictionaryUtils.getHeader(file); @@ -276,7 +275,7 @@ final public class BinaryDictionaryGetter { // cachedWordLists may not be null, see doc for getCachedDictionaryList for (final File f : cachedWordLists) { final String wordListId = DictionaryInfoUtils.getWordListIdFromFileName(f.getName()); - final boolean canUse = f.canRead() && hackCanUseDictionaryFile(locale, f); + final boolean canUse = f.canRead() && hackCanUseDictionaryFile(f); if (canUse && DictionaryInfoUtils.isMainWordListId(wordListId)) { foundMainDict = true; } diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 78c6cbd24..59763c0fc 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -26,13 +26,13 @@ import android.os.SystemClock; import android.provider.BaseColumns; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; -import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.annotations.ExternallyReferenced; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.personalization.AccountUtils; import com.android.inputmethod.latin.utils.ExecutorUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.io.File; import java.util.ArrayList; @@ -83,7 +83,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { reloadDictionaryIfRequired(); } - @UsedForTesting + // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. + @ExternallyReferenced public static ContactsBinaryDictionary getDictionary(final Context context, final Locale locale, final File dictFile, final String dictNamePrefix) { return new ContactsBinaryDictionary(context, locale, dictFile, dictNamePrefix + NAME); @@ -137,7 +138,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } runGCIfRequiredLocked(true /* mindsBlockByGC */); addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */, - 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, + 0 /* shortcutFreq */, false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); } } @@ -164,7 +165,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } } - private boolean useFirstLastBigramsForLocale(final Locale locale) { + private static boolean useFirstLastBigramsForLocale(final Locale locale) { // TODO: Add firstname/lastname bigram rules for other languages. if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { return true; @@ -238,7 +239,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { runGCIfRequiredLocked(true /* mindsBlockByGC */); addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */, - false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); if (!ngramContext.isValid() && mUseFirstLastBigrams) { runGCIfRequiredLocked(true /* mindsBlockByGC */); addNgramEntryLocked(ngramContext, word, FREQUENCY_FOR_CONTACTS_BIGRAM, @@ -268,7 +270,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { return end; } - private boolean haveContentsChanged() { + boolean haveContentsChanged() { final long startTime = SystemClock.uptimeMillis(); final int contactCount = getContactCount(); if (contactCount > MAX_CONTACT_COUNT) { diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index 2751c1250..95390aa9f 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.utils.JniUtils; @@ -70,7 +71,7 @@ public final class DicTraverseSession { mNativeDicTraverseSession, dictionary, previousWord, previousWordLength); } - private final long createNativeDicTraverseSession(String locale, long dictSize) { + private static long createNativeDicTraverseSession(String locale, long dictSize) { return setDicTraverseSessionNative(locale, dictSize); } diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index e66847b56..7d7ed77e7 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -17,8 +17,8 @@ package com.android.inputmethod.latin; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; @@ -72,9 +72,13 @@ public abstract class Dictionary { * Set out of the dictionary types listed above that are based on data specific to the user, * e.g., the user's contacts. */ - private static final HashSet<String> sUserSpecificDictionaryTypes = - new HashSet(Arrays.asList(new String[] { TYPE_USER_TYPED, TYPE_USER, TYPE_CONTACTS, - TYPE_USER_HISTORY, TYPE_PERSONALIZATION, TYPE_CONTEXTUAL })); + private static final HashSet<String> sUserSpecificDictionaryTypes = new HashSet<>(Arrays.asList( + TYPE_USER_TYPED, + TYPE_USER, + TYPE_CONTACTS, + TYPE_USER_HISTORY, + TYPE_PERSONALIZATION, + TYPE_CONTEXTUAL)); public Dictionary(final String dictType, final Locale locale) { mDictType = dictType; @@ -83,9 +87,9 @@ public abstract class Dictionary { /** * Searches for suggestions for a given context. - * @param composer the key sequence to match with coordinate info, as a WordComposer + * @param composedData the key sequence to match with coordinate info * @param ngramContext the context for n-gram. - * @param proximityInfo the object for key proximity. May be ignored by some implementations. + * @param proximityInfoHandle the handle for key proximity. Is ignored by some implementations. * @param settingsValuesForSuggestion the settings values used for the suggestion. * @param sessionId the session id. * @param weightForLocale the weight given to this locale, to multiply the output scores for @@ -95,8 +99,8 @@ public abstract class Dictionary { * a float array that has only one element. This can be updated when a different value is used. * @return the list of suggestions (possibly null if none) */ - abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + abstract public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel); @@ -116,10 +120,18 @@ public abstract class Dictionary { */ abstract public boolean isInDictionary(final String word); + /** + * Get the frequency of the word. + * @param word the word to get the frequency of. + */ public int getFrequency(final String word) { return NOT_A_PROBABILITY; } + /** + * Get the maximum frequency of the word. + * @param word the word to get the maximum frequency of. + */ public int getMaxFrequencyOfExactMatches(final String word) { return NOT_A_PROBABILITY; } @@ -191,8 +203,8 @@ public abstract class Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index a6d7205e2..96575f629 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -18,8 +18,8 @@ package com.android.inputmethod.latin; import android.util.Log; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; @@ -59,8 +59,8 @@ public final class DictionaryCollection extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { @@ -68,15 +68,15 @@ public final class DictionaryCollection extends Dictionary { if (dictionaries.isEmpty()) return null; // To avoid creating unnecessary objects, we get the list out of the first // dictionary and add the rest to it if not null, hence the get(0) - ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer, - ngramContext, proximityInfo, settingsValuesForSuggestion, sessionId, + ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composedData, + ngramContext, proximityInfoHandle, settingsValuesForSuggestion, sessionId, weightForLocale, inOutWeightOfLangModelVsSpatialModel); if (null == suggestions) suggestions = new ArrayList<>(); final int length = dictionaries.size(); for (int i = 1; i < length; ++ i) { - final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer, - ngramContext, proximityInfo, settingsValuesForSuggestion, sessionId, - weightForLocale, inOutWeightOfLangModelVsSpatialModel); + final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions( + composedData, ngramContext, proximityInfoHandle, settingsValuesForSuggestion, + sessionId, weightForLocale, inOutWeightOfLangModelVsSpatialModel); if (null != sugg) suggestions.addAll(sugg); } return suggestions; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 08035dfd6..d23639a0d 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -23,10 +23,10 @@ import android.util.Pair; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; -import com.android.inputmethod.latin.ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback; +import com.android.inputmethod.latin.ExpandableBinaryDictionary.UpdateEntriesForInputEventsCallback; import com.android.inputmethod.latin.NgramContext.WordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.personalization.ContextualDictionary; import com.android.inputmethod.latin.personalization.PersonalizationDataChunk; import com.android.inputmethod.latin.personalization.PersonalizationDictionary; @@ -53,6 +53,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + // TODO: Consolidate dictionaries in native code. public class DictionaryFacilitator { public static final String TAG = DictionaryFacilitator.class.getSimpleName(); @@ -172,9 +175,8 @@ public class DictionaryFacilitator { public Dictionary getDict(final String dictType) { if (Dictionary.TYPE_MAIN.equals(dictType)) { return mMainDict; - } else { - return getSubDict(dictType); } + return getSubDict(dictType); } public ExpandableBinaryDictionary getSubDict(final String dictType) { @@ -184,9 +186,8 @@ public class DictionaryFacilitator { public boolean hasDict(final String dictType) { if (Dictionary.TYPE_MAIN.equals(dictType)) { return mMainDict != null; - } else { - return mSubDictMap.containsKey(dictType); } + return mSubDictMap.containsKey(dictType); } public void closeDict(final String dictType) { @@ -276,6 +277,7 @@ public class DictionaryFacilitator { mMostProbableDictionaryGroup = newMostProbableDictionaryGroup; } + @Nullable private static ExpandableBinaryDictionary getSubDict(final String dictType, final Context context, final Locale locale, final File dictFile, final String dictNamePrefix) { @@ -305,7 +307,8 @@ public class DictionaryFacilitator { usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */); } - private DictionaryGroup findDictionaryGroupWithLocale(final DictionaryGroup[] dictionaryGroups, + @Nullable + static DictionaryGroup findDictionaryGroupWithLocale(final DictionaryGroup[] dictionaryGroups, final Locale locale) { for (int i = 0; i < dictionaryGroups.length; ++i) { if (locale.equals(dictionaryGroups[i].mLocale)) { @@ -319,7 +322,7 @@ public class DictionaryFacilitator { final Locale[] newLocales, final boolean useContactsDict, final boolean usePersonalizedDicts, final boolean forceReloadMainDictionary, - final DictionaryInitializationListener listener, + @Nullable final DictionaryInitializationListener listener, final String dictNamePrefix) { final HashMap<Locale, ArrayList<String>> existingDictsToCleanup = new HashMap<>(); // TODO: Make subDictTypesToUse configurable by resource or a static final list. @@ -422,34 +425,41 @@ public class DictionaryFacilitator { ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() { @Override public void run() { - for (final Locale locale : locales) { - final DictionaryGroup dictionaryGroup = - findDictionaryGroupWithLocale(mDictionaryGroups, locale); - if (null == dictionaryGroup) { - // This should never happen, but better safe than crashy - Log.w(TAG, "Expected a dictionary group for " + locale + " but none found"); - continue; - } - final Dictionary mainDict = - DictionaryFactory.createMainDictionaryFromManager(context, locale); - synchronized (mLock) { - if (locale.equals(dictionaryGroup.mLocale)) { - dictionaryGroup.setMainDict(mainDict); - } else { - // Dictionary facilitator has been reset for another locale. - mainDict.close(); - } - } - } - if (listener != null) { - listener.onUpdateMainDictionaryAvailability( - hasAtLeastOneInitializedMainDictionary()); - } - latchForWaitingLoadingMainDictionary.countDown(); + doReloadUninitializedMainDictionaries( + context, locales, listener, latchForWaitingLoadingMainDictionary); } }); } + void doReloadUninitializedMainDictionaries(final Context context, final Locale[] locales, + final DictionaryInitializationListener listener, + final CountDownLatch latchForWaitingLoadingMainDictionary) { + for (final Locale locale : locales) { + final DictionaryGroup dictionaryGroup = + findDictionaryGroupWithLocale(mDictionaryGroups, locale); + if (null == dictionaryGroup) { + // This should never happen, but better safe than crashy + Log.w(TAG, "Expected a dictionary group for " + locale + " but none found"); + continue; + } + final Dictionary mainDict = + DictionaryFactory.createMainDictionaryFromManager(context, locale); + synchronized (mLock) { + if (locale.equals(dictionaryGroup.mLocale)) { + dictionaryGroup.setMainDict(mainDict); + } else { + // Dictionary facilitator has been reset for another locale. + mainDict.close(); + } + } + } + if (listener != null) { + listener.onUpdateMainDictionaryAvailability( + hasAtLeastOneInitializedMainDictionary()); + } + latchForWaitingLoadingMainDictionary.countDown(); + } + @UsedForTesting public void resetDictionariesForTesting(final Context context, final Locale[] locales, final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles, @@ -588,7 +598,7 @@ public class DictionaryFacilitator { } public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, - final NgramContext ngramContext, final int timeStampInSeconds, + @Nonnull final NgramContext ngramContext, final int timeStampInSeconds, final boolean blockPotentiallyOffensive) { final DictionaryGroup dictionaryGroup = getDictionaryGroupForMostProbableLanguage(); final String[] words = suggestion.split(Constants.WORD_SEPARATOR); @@ -672,7 +682,7 @@ public class DictionaryFacilitator { // TODO: Revise the way to fusion suggestion results. public SuggestionResults getSuggestionResults(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; final SuggestionResults suggestionResults = new SuggestionResults( @@ -687,8 +697,8 @@ public class DictionaryFacilitator { ? dictionaryGroup.mWeightForGesturingInLocale : dictionaryGroup.mWeightForTypingInLocale; final ArrayList<SuggestedWordInfo> dictionarySuggestions = - dictionary.getSuggestions(composer, ngramContext, proximityInfo, - settingsValuesForSuggestion, sessionId, + dictionary.getSuggestions(composer.getComposedDataSnapshot(), ngramContext, + proximityInfoHandle, settingsValuesForSuggestion, sessionId, weightForLocale, weightOfLangModelVsSpatialModel); if (null == dictionarySuggestions) continue; suggestionResults.addAll(dictionarySuggestions); @@ -786,8 +796,8 @@ public class DictionaryFacilitator { public void addEntriesToPersonalizationDictionary( final PersonalizationDataChunk personalizationDataChunk, final SpacingAndPunctuations spacingAndPunctuations, - final AddMultipleDictionaryEntriesCallback callback) { - mPersonalizationHelper.addEntriesToPersonalizationDictionariesToUpdate( + final UpdateEntriesForInputEventsCallback callback) { + mPersonalizationHelper.updateEntriesOfPersonalizationDictionaries( getMostProbableLocale(), personalizationDataChunk, spacingAndPunctuations, callback); } @@ -809,7 +819,7 @@ public class DictionaryFacilitator { contextualDict.addUnigramEntryWithCheckingDistracter( subPhraseStr, probability, null /* shortcutTarget */, Dictionary.NOT_A_PROBABILITY /* shortcutFreq */, - false /* isNotAWord */, false /* isBlacklisted */, + false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP, DistracterFilter.EMPTY_DISTRACTER_FILTER); contextualDict.addNgramEntry(ngramContext, subPhraseStr, @@ -819,7 +829,7 @@ public class DictionaryFacilitator { contextualDict.addUnigramEntryWithCheckingDistracter( phrase[i], probability, null /* shortcutTarget */, Dictionary.NOT_A_PROBABILITY /* shortcutFreq */, - false /* isNotAWord */, false /* isBlacklisted */, + false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP, DistracterFilter.EMPTY_DISTRACTER_FILTER); contextualDict.addNgramEntry(ngramContext, phrase[i], diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java index 1b33d9129..b578159eb 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java @@ -31,7 +31,7 @@ import android.util.LruCache; * This class automatically creates and releases facilitator instances using LRU policy. */ public class DictionaryFacilitatorLruCache { - private static final String TAG = DictionaryFacilitatorLruCache.class.getSimpleName(); + static final String TAG = DictionaryFacilitatorLruCache.class.getSimpleName(); private static final int WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS = 1000; private static final int MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT = 5; @@ -81,7 +81,8 @@ public class DictionaryFacilitatorLruCache { mDictionaryNamePrefix = dictionaryNamePrefix; } - private void waitForLoadingMainDictionary(final DictionaryFacilitator dictionaryFacilitator) { + private static void waitForLoadingMainDictionary( + final DictionaryFacilitator dictionaryFacilitator) { for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) { try { dictionaryFacilitator.waitForLoadingMainDictionaries( diff --git a/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java new file mode 100644 index 000000000..8116a4983 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java @@ -0,0 +1,93 @@ +/* + * 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; + +import android.view.KeyEvent; + +import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.settings.Settings; + +/** + * A class for detecting Emoji-Alt physical key. + */ +final class EmojiAltPhysicalKeyDetector { + // True if the Alt key has been used as a modifier. In this case the Alt key up isn't + // recognized as an emoji key. + private boolean mAltHasBeenUsedAsAModifier; + + /** + * Record a down key event. + * @param keyEvent a down key event. + */ + public void onKeyDown(final KeyEvent keyEvent) { + if (isAltKey(keyEvent)) { + mAltHasBeenUsedAsAModifier = false; + } + if (containsAltModifier(keyEvent)) { + mAltHasBeenUsedAsAModifier = true; + } + } + + /** + * Determine whether an up key event is a special key up or not. + * @param keyEvent an up key event. + */ + public void onKeyUp(final KeyEvent keyEvent) { + if (keyEvent.isCanceled()) { + // This key up event was a part of key combinations and should be ignored. + return; + } + if (!isAltKey(keyEvent)) { + mAltHasBeenUsedAsAModifier |= containsAltModifier(keyEvent); + return; + } + if (containsAltModifier(keyEvent)) { + mAltHasBeenUsedAsAModifier = true; + return; + } + if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) { + return; + } + if (!mAltHasBeenUsedAsAModifier) { + onEmojiAltKeyDetected(); + } + } + + private static void onEmojiAltKeyDetected() { + KeyboardSwitcher.getInstance().onToggleEmojiKeyboard(); + } + + private static boolean isAltKey(final KeyEvent keyEvent) { + final int keyCode = keyEvent.getKeyCode(); + return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT; + } + + private static boolean containsAltModifier(final KeyEvent keyEvent) { + final int metaState = keyEvent.getMetaState(); + // TODO: Support multiple keyboards. Take device id into account. + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_ALT_LEFT: + // Return true if Left-Alt is pressed with Right-Alt pressed. + return (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0; + case KeyEvent.KEYCODE_ALT_RIGHT: + // Return true if Right-Alt is pressed with Left-Alt pressed. + return (metaState & KeyEvent.META_ALT_LEFT_ON) != 0; + default: + return (metaState & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0; + } + } +} diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index d24f80a45..b47eaa9bb 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -20,19 +20,20 @@ import android.content.Context; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.WordProperty; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CombinedFormatUtils; import com.android.inputmethod.latin.utils.DistracterFilter; import com.android.inputmethod.latin.utils.ExecutorUtils; import com.android.inputmethod.latin.utils.FileUtils; -import com.android.inputmethod.latin.utils.LanguageModelParam; +import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; import java.io.File; import java.util.ArrayList; @@ -47,11 +48,16 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * Abstract base class for an expandable dictionary that can be created and updated dynamically * during runtime. When updated it automatically generates a new binary dictionary to handle future * queries in native code. This binary dictionary is written to internal storage. + * + * A class that extends this abstract class must have a static factory method named + * getDictionary(Context context, Locale locale, File dictFile, String dictNamePrefix) + * @see DictionaryFacilitator#getSubDict(String,Context,Locale,File,String) */ abstract public class ExpandableBinaryDictionary extends Dictionary { private static final boolean DEBUG = false; @@ -64,9 +70,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; - private static final int DEFAULT_MAX_UNIGRAM_COUNT = 10000; - private static final int DEFAULT_MAX_BIGRAM_COUNT = 10000; - /** * The maximum length of a word in this dictionary. */ @@ -110,14 +113,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected abstract void loadInitialContentsLocked(); - private boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) { + static boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) { return formatVersion == FormatSpec.VERSION4; } - private boolean needsToMigrateDictionary(final int formatVersion) { + private static boolean needsToMigrateDictionary(final int formatVersion) { // When we bump up the dictionary format version, the old version should be added to here // for supporting migration. Note that native code has to support reading such formats. - return formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING; + return formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING + || formatVersion == FormatSpec.VERSION402; } public boolean isValidDictionaryLocked() { @@ -162,7 +166,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithLock(mLock.writeLock(), mDictName /* executorName */, task); } - private void asyncExecuteTaskWithLock(final Lock lock, final String executorName, + private static void asyncExecuteTaskWithLock(final Lock lock, final String executorName, final Runnable task) { asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, executorName, task); } @@ -175,8 +179,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } // Execute task with lock when the result of preCheckTask is true or preCheckTask is null. - private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, + private static void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, final Callable<Boolean> preCheckTask, final String executorName, final Runnable task) { + final String tag = TAG; ExecutorUtils.getExecutor(executorName).execute(new Runnable() { @Override public void run() { @@ -186,7 +191,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return; } } catch (final Exception e) { - Log.e(TAG, "The pre check task throws an exception.", e); + Log.e(tag, "The pre check task throws an exception.", e); return; } } @@ -200,6 +205,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }); } + @Nullable + BinaryDictionary getBinaryDictionary() { + return mBinaryDictionary; + } + + void closeBinaryDictionary() { + if (mBinaryDictionary != null) { + mBinaryDictionary.close(); + mBinaryDictionary = null; + } + } + /** * Closes and cleans up the binary dictionary. */ @@ -208,10 +225,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary != null) { - mBinaryDictionary.close(); - mBinaryDictionary = null; - } + closeBinaryDictionary(); } }); } @@ -225,10 +239,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString()); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); - attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, - String.valueOf(DEFAULT_MAX_UNIGRAM_COUNT)); - attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, - String.valueOf(DEFAULT_MAX_BIGRAM_COUNT)); return attributeMap; } @@ -241,14 +251,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }); } - private void removeBinaryDictionaryLocked() { - if (mBinaryDictionary != null) { - mBinaryDictionary.close(); - } + void removeBinaryDictionaryLocked() { + closeBinaryDictionary(); if (mDictFile.exists() && !FileUtils.deleteRecursively(mDictFile)) { Log.e(TAG, "Can't remove a file: " + mDictFile.getName()); } - mBinaryDictionary = null; } private void openBinaryDictionaryLocked() { @@ -257,7 +264,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { true /* useFullEditDistance */, mLocale, mDictType, true /* isUpdatable */); } - private void createOnMemoryBinaryDictionaryLocked() { + void createOnMemoryBinaryDictionaryLocked() { mBinaryDictionary = new BinaryDictionary( mDictFile.getAbsolutePath(), true /* useFullEditDistance */, mLocale, mDictType, DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); @@ -280,7 +287,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + if (getBinaryDictionary() == null) { return; } runGCIfRequiredLocked(mindsBlockByGC); @@ -298,24 +305,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Nonnull final Runnable updateTask, @Nonnull final String word, @Nonnull final DistracterFilter distracterFilter) { reloadDictionaryIfRequired(); - asyncPreCheckAndExecuteTaskWithWriteLock( - new Callable<Boolean>() { - @Override - public Boolean call() throws Exception { - return !distracterFilter.isDistracterToWordsInDictionaries( - NgramContext.EMPTY_PREV_WORDS_INFO, word, mLocale); - } - }, - new Runnable() { - @Override - public void run() { - if (mBinaryDictionary == null) { - return; - } - runGCIfRequiredLocked(true /* mindsBlockByGC */); - updateTask.run(); - } - }); + final Callable<Boolean> preCheckTask = new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + return !distracterFilter.isDistracterToWordsInDictionaries( + NgramContext.EMPTY_PREV_WORDS_INFO, word, mLocale); + } + }; + final Runnable task = new Runnable() { + @Override + public void run() { + if (getBinaryDictionary() == null) { + return; + } + runGCIfRequiredLocked(true /* mindsBlockByGC */); + updateTask.run(); + } + }; + asyncPreCheckAndExecuteTaskWithWriteLock(preCheckTask, task); } /** @@ -323,22 +330,22 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, - final boolean isBlacklisted, final int timestamp, + final boolean isPossiblyOffensive, final int timestamp, @Nonnull final DistracterFilter distracterFilter) { updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() { @Override public void run() { addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, - isNotAWord, isBlacklisted, timestamp); + isNotAWord, isPossiblyOffensive, timestamp); } }, word, distracterFilter); } protected void addUnigramLocked(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, - final boolean isBlacklisted, final int timestamp) { + final boolean isPossiblyOffensive, final int timestamp) { if (!mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq, - false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, timestamp)) { + false /* isBeginningOfSentence */, isNotAWord, isPossiblyOffensive, timestamp)) { Log.e(TAG, "Cannot add unigram entry. word: " + word); } } @@ -351,11 +358,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); - if (!mBinaryDictionary.removeUnigramEntry(word)) { + if (!binaryDictionary.removeUnigramEntry(word)) { if (DEBUG) { Log.i(TAG, "Cannot remove unigram entry: " + word); } @@ -373,7 +381,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + if (getBinaryDictionary() == null) { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); @@ -402,11 +410,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); - if (!mBinaryDictionary.removeNgramEntry(ngramContext, word)) { + if (!binaryDictionary.removeNgramEntry(ngramContext, word)) { if (DEBUG) { Log.i(TAG, "Cannot remove n-gram entry."); Log.i(TAG, " NgramContext: " + ngramContext + ", word: " + word); @@ -425,7 +434,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() { @Override public void run() { - if (!mBinaryDictionary.updateEntriesForWordWithNgramContext(ngramContext, word, + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { + return; + } + if (!binaryDictionary.updateEntriesForWordWithNgramContext(ngramContext, word, isValidWord, count, timestamp)) { if (DEBUG) { Log.e(TAG, "Cannot update counter. word: " + word @@ -436,27 +449,28 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }, word, distracterFilter); } - public interface AddMultipleDictionaryEntriesCallback { + public interface UpdateEntriesForInputEventsCallback { public void onFinished(); } /** - * Dynamically add multiple entries to the dictionary. + * Dynamically update entries according to input events. */ - public void addMultipleDictionaryEntriesDynamically( - @Nonnull final ArrayList<LanguageModelParam> languageModelParams, - final AddMultipleDictionaryEntriesCallback callback) { + public void updateEntriesForInputEvents( + @Nonnull final ArrayList<WordInputEventForPersonalization> inputEvents, + final UpdateEntriesForInputEventsCallback callback) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { try { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } - mBinaryDictionary.addMultipleDictionaryEntries( - languageModelParams.toArray( - new LanguageModelParam[languageModelParams.size()])); + binaryDictionary.updateEntriesForInputEvents( + inputEvents.toArray( + new WordInputEventForPersonalization[inputEvents.size()])); } finally { if (callback != null) { callback.onFinished(); @@ -467,8 +481,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { reloadDictionaryIfRequired(); @@ -481,9 +495,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return null; } final ArrayList<SuggestedWordInfo> suggestions = - mBinaryDictionary.getSuggestions(composer, ngramContext, proximityInfo, - settingsValuesForSuggestion, sessionId, weightForLocale, - inOutWeightOfLangModelVsSpatialModel); + mBinaryDictionary.getSuggestions(composedData, ngramContext, + proximityInfoHandle, settingsValuesForSuggestion, sessionId, + weightForLocale, inOutWeightOfLangModelVsSpatialModel); if (mBinaryDictionary.isCorrupted()) { Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. " + "Remove and regenerate it."); @@ -562,7 +576,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Loads the current binary dictionary from internal storage. Assumes the dictionary file * exists. */ - private void loadBinaryDictionaryLocked() { + void loadBinaryDictionaryLocked() { if (DBG_STRESS_TEST) { // Test if this class does not cause problems when it takes long time to load binary // dictionary. @@ -590,7 +604,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** * Create a new binary dictionary and load initial contents. */ - private void createNewDictionaryLocked() { + void createNewDictionaryLocked() { removeBinaryDictionaryLocked(); createOnMemoryBinaryDictionaryLocked(); loadInitialContentsLocked(); @@ -606,6 +620,14 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mNeedsToRecreate = true; } + void clearNeedsToRecreate() { + mNeedsToRecreate = false; + } + + boolean isNeededToRecreate() { + return mNeedsToRecreate; + } + /** * Load the current binary dictionary from internal storage. If the dictionary file doesn't * exists or needs to be regenerated, the new dictionary file will be asynchronously generated. @@ -628,35 +650,39 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Reloads the dictionary. Access is controlled on a per dictionary file basis. */ private final void asyncReloadDictionary() { - if (mIsReloading.compareAndSet(false, true)) { - asyncExecuteTaskWithWriteLock(new Runnable() { - @Override - public void run() { - try { - if (!mDictFile.exists() || mNeedsToRecreate) { - // If the dictionary file does not exist or contents have been updated, - // generate a new one. + final AtomicBoolean isReloading = mIsReloading; + if (!isReloading.compareAndSet(false, true)) { + return; + } + final File dictFile = mDictFile; + asyncExecuteTaskWithWriteLock(new Runnable() { + @Override + public void run() { + try { + if (!dictFile.exists() || isNeededToRecreate()) { + // If the dictionary file does not exist or contents have been updated, + // generate a new one. + createNewDictionaryLocked(); + } else if (getBinaryDictionary() == null) { + // Otherwise, load the existing dictionary. + loadBinaryDictionaryLocked(); + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary != null && !(isValidDictionaryLocked() + // TODO: remove the check below + && matchesExpectedBinaryDictFormatVersionForThisType( + binaryDictionary.getFormatVersion()))) { + // Binary dictionary or its format version is not valid. Regenerate + // the dictionary file. createNewDictionaryLocked will remove the + // existing files if appropriate. createNewDictionaryLocked(); - } else if (mBinaryDictionary == null) { - // Otherwise, load the existing dictionary. - loadBinaryDictionaryLocked(); - if (mBinaryDictionary != null && !(isValidDictionaryLocked() - // TODO: remove the check below - && matchesExpectedBinaryDictFormatVersionForThisType( - mBinaryDictionary.getFormatVersion()))) { - // Binary dictionary or its format version is not valid. Regenerate - // the dictionary file. createNewDictionaryLocked will remove the - // existing files if appropriate. - createNewDictionaryLocked(); - } } - mNeedsToRecreate = false; - } finally { - mIsReloading.set(false); } + clearNeedsToRecreate(); + } finally { + isReloading.set(false); } - }); - } + } + }); } /** @@ -666,19 +692,20 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } - if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { - mBinaryDictionary.flushWithGC(); + if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { + binaryDictionary.flushWithGC(); } else { - mBinaryDictionary.flush(); + binaryDictionary.flush(); } } }); } - private static int parseEntryCount(final String entryCountStr) { + static int parseEntryCount(final String entryCountStr) { int entryCount; try { entryCount = Integer.parseInt(entryCountStr); @@ -690,23 +717,27 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public DictionaryStats getDictionaryStats() { reloadDictionaryIfRequired(); + final String dictName = mDictName; + final File dictFile = mDictFile; final AsyncResultHolder<DictionaryStats> result = new AsyncResultHolder<>(); - asyncExecuteTaskWithLock(mLock.readLock(), mDictName /* executorName */, new Runnable() { + asyncExecuteTaskWithLock(mLock.readLock(), dictName /* executorName */, new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { - result.set(new DictionaryStats(mLocale, mDictName, mDictFile, + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { + result.set(new DictionaryStats(mLocale, dictName, dictFile, DictionaryStats.NOT_AN_ENTRY_COUNT, DictionaryStats.NOT_AN_ENTRY_COUNT)); + return; } final int unigramCount = parseEntryCount( - mBinaryDictionary.getPropertyForGettingStats( + binaryDictionary.getPropertyForGettingStats( BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); // TODO: Get dedicated entry counts for bigram, trigram, and so on. - final int ngramCount = parseEntryCount(mBinaryDictionary.getPropertyForGettingStats( + final int ngramCount = parseEntryCount(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.MAX_BIGRAM_COUNT_QUERY)); // TODO: Get more information from dictionary. - result.set(new DictionaryStats(mLocale, mDictName, mDictFile, unigramCount, + result.set(new DictionaryStats(mLocale, dictName, dictFile, unigramCount, ngramCount)); } }); @@ -738,28 +769,34 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void dumpAllWordsForDebug() { reloadDictionaryIfRequired(); + final String tag = TAG; + final String dictName = mDictName; asyncExecuteTaskWithLock(mLock.readLock(), "dumpAllWordsForDebug", new Runnable() { @Override public void run() { - Log.d(TAG, "Dump dictionary: " + mDictName + " for " + mLocale); + Log.d(tag, "Dump dictionary: " + dictName + " for " + mLocale); + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { + return; + } try { - final DictionaryHeader header = mBinaryDictionary.getHeader(); - Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); - Log.d(TAG, CombinedFormatUtils.formatAttributeMap( + final DictionaryHeader header = binaryDictionary.getHeader(); + Log.d(tag, "Format version: " + binaryDictionary.getFormatVersion()); + Log.d(tag, CombinedFormatUtils.formatAttributeMap( header.mDictionaryOptions.mAttributes)); } catch (final UnsupportedFormatException e) { - Log.d(TAG, "Cannot fetch header information.", e); + Log.d(tag, "Cannot fetch header information.", e); } int token = 0; do { final BinaryDictionary.GetNextWordPropertyResult result = - mBinaryDictionary.getNextWordProperty(token); + binaryDictionary.getNextWordProperty(token); final WordProperty wordProperty = result.mWordProperty; if (wordProperty == null) { - Log.d(TAG, " dictionary is empty."); + Log.d(tag, " dictionary is empty."); break; } - Log.d(TAG, wordProperty.toString()); + Log.d(tag, wordProperty.toString()); token = result.mNextToken; } while (token != 0); } diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index ffd363b5d..37effeead 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -16,16 +16,16 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_FLOATING_GESTURE_PREVIEW; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_FLOATING_GESTURE_PREVIEW; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT; import android.text.InputType; import android.util.Log; import android.view.inputmethod.EditorInfo; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java index 7fa935413..f3a8ca169 100644 --- a/java/src/com/android/inputmethod/latin/InputView.java +++ b/java/src/com/android/inputmethod/latin/InputView.java @@ -139,7 +139,10 @@ public final class InputView extends FrameLayout { return y - mEventReceivingRect.top; } - // Callback when a {@link MotionEvent} is forwarded. + /** + * Callback when a {@link MotionEvent} is forwarded. + * @param me the motion event to be forwarded. + */ protected void onForwardingEvent(final MotionEvent me) {} // Returns true if a {@link MotionEvent} is needed to be forwarded to diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index f3f736fbc..9fcdb2229 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -19,6 +19,8 @@ package com.android.inputmethod.latin; import android.text.TextUtils; import com.android.inputmethod.event.Event; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ec3d89583..dc665471d 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -16,10 +16,11 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; +import static com.android.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE; +import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT; +import android.annotation.TargetApi; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -75,6 +76,8 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.TextDecoratorUi; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.define.ProductionFlags; import com.android.inputmethod.latin.inputlogic.InputLogic; @@ -119,15 +122,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen SuggestionStripView.Listener, SuggestionStripViewAccessor, DictionaryFacilitator.DictionaryInitializationListener, ImportantNoticeDialog.ImportantNoticeDialogListener { - private static final String TAG = LatinIME.class.getSimpleName(); + static final String TAG = LatinIME.class.getSimpleName(); private static final boolean TRACE = false; private static boolean DEBUG = false; private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100; private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2; private static final int PENDING_IMS_CALLBACK_DURATION_MILLIS = 800; - private static final long DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS = TimeUnit.SECONDS.toMillis(2); - private static final long DELAY_DEALLOCATE_MEMORY_MILLIS = TimeUnit.SECONDS.toMillis(10); + static final long DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS = TimeUnit.SECONDS.toMillis(2); + static final long DELAY_DEALLOCATE_MEMORY_MILLIS = TimeUnit.SECONDS.toMillis(10); /** * The name of the scheme used by the Package Manager to warn of a new package installation, @@ -135,7 +138,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ private static final String SCHEME_PACKAGE = "package"; - private final Settings mSettings; + final Settings mSettings; private final DictionaryFacilitator mDictionaryFacilitator = new DictionaryFacilitator(this /* context */); // TODO: Move from LatinIME. @@ -149,7 +152,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE); } }); - private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */, + final InputLogic mInputLogic = new InputLogic(this /* LatinIME */, this /* SuggestionStripViewAccessor */, mDictionaryFacilitator); // We expect to have only one decoder in almost all cases, hence the default capacity of 1. // If it turns out we need several, it will get grown seamlessly. @@ -163,9 +166,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private RichInputMethodManager mRichImm; @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; - private final SubtypeSwitcher mSubtypeSwitcher; + final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); - private final SpecialKeyDetector mSpecialKeyDetector; + private final EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector = + new EmojiAltPhysicalKeyDetector(); private StatsUtilsManager mStatsUtilsManager; // Working variable for {@link #startShowingInputView()} and // {@link #onEvaluateInputViewShown()}. @@ -204,7 +208,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2; private static final int ARG2_UNUSED = 0; - private static final int ARG1_FALSE = 0; private static final int ARG1_TRUE = 1; private int mDelayInMillisecondsToUpdateSuggestions; @@ -544,7 +547,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSettings = Settings.getInstance(); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); - mSpecialKeyDetector = new SpecialKeyDetector(this); mStatsUtilsManager = StatsUtilsManager.getInstance(); mIsHardwareAcceleratedDrawingEnabled = InputMethodServiceCompatUtils.enableHardwareAcceleration(this); @@ -654,7 +656,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private void resetDictionaryFacilitatorIfNecessary() { + void resetDictionaryFacilitatorIfNecessary() { final Locale[] subtypeSwitcherLocales = mSubtypeSwitcher.getCurrentSubtypeLocales(); if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales)) { return; @@ -791,17 +793,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } + void updateCursorAnchorInfo() { + // CursorAnchorInfo is used on L and later. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (isFullscreenMode() && mExtractEditText != null) { + mInputLogic.onUpdateCursorAnchorInfo( + CursorAnchorInfoUtils.extractFromTextView(mExtractEditText)); + } + } + } + private final ViewTreeObserver.OnPreDrawListener mExtractTextViewPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - // CursorAnchorInfo is used on L and later. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.L) { - if (isFullscreenMode() && mExtractEditText != null) { - mInputLogic.onUpdateCursorAnchorInfo( - CursorAnchorInfoUtils.extractFromTextView(mExtractEditText)); - } - } + updateCursorAnchorInfo(); return true; } }; @@ -846,12 +852,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen loadKeyboard(); } - private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) { + void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInput(editorInfo, restarting); } @SuppressWarnings("deprecation") - private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) { + void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInputView(editorInfo, restarting); // Switch to the null consumer to handle cases leading to early exit below, for which we // also wouldn't be consuming gesture data. @@ -1034,7 +1040,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private void onFinishInputInternal() { + void onFinishInputInternal() { super.onFinishInput(); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); @@ -1043,7 +1049,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private void onFinishInputViewInternal(final boolean finishingInput) { + void onFinishInputViewInternal(final boolean finishingInput) { super.onFinishInputView(finishingInput); cleanupInternalStateForFinishInput(); } @@ -1071,11 +1077,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd); } - // This call happens when we have a hardware keyboard as well as when we don't. While we - // don't support hardware keyboards yet we should avoid doing the processing associated - // with cursor movement when we have a hardware keyboard since we are not in charge. + // This call happens whether our view is displayed or not, but if it's not then we should + // not attempt recorrection. This is true even with a hardware keyboard connected: if the + // view is not displayed we have no means of showing suggestions anyway, and if it is then + // we want to show suggestions anyway. final SettingsValues settingsValues = mSettings.getCurrent(); - if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) + if (isInputViewShown() && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, settingsValues)) { mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), @@ -1083,8 +1090,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - // We cannot mark this method as @Override until new SDK becomes publicly available. - // @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override public void onUpdateCursorAnchorInfo(final CursorAnchorInfo info) { if (isFullscreenMode()) { return; @@ -1188,7 +1195,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) { // If there is a hardware keyboard and a visible software keyboard view has been hidden, // no visual element will be shown on the screen. - outInsets.touchableInsets = inputHeight; + outInsets.contentTopInsets = inputHeight; outInsets.visibleTopInsets = inputHeight; mInsetsUpdater.setInsets(outInsets); return; @@ -1198,7 +1205,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ? mSuggestionStripView.getHeight() : 0; final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight; mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY); - // Need to set touchable region only if a keyboard view is being shown. + // Need to set expanded touchable region only if a keyboard view is being shown. if (visibleKeyboardView.isShown()) { final int touchLeft = 0; final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY; @@ -1295,11 +1302,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private int getCurrentAutoCapsState() { + int getCurrentAutoCapsState() { return mInputLogic.getCurrentAutoCapsState(mSettings.getCurrent()); } - private int getCurrentRecapitalizeState() { + int getCurrentRecapitalizeState() { return mInputLogic.getCurrentRecapitalizeState(); } @@ -1389,12 +1396,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Keyboard currentKeyboard = mKeyboardSwitcher.getKeyboard(); if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) { return codePoint; - } else { - return Constants.CODE_SYMBOL_SHIFT; } - } else { - return codePoint; + return Constants.CODE_SYMBOL_SHIFT; } + return codePoint; } // Implementation of {@link KeyboardActionListener}. @@ -1417,7 +1422,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This method is public for testability of LatinIME, but also in the future it should // completely replace #onCodeInput. - public void onEvent(final Event event) { + public void onEvent(@Nonnull final Event event) { if (Constants.CODE_SHORTCUT == event.mKeyCode) { mSubtypeSwitcher.switchToShortcutIME(this); } @@ -1432,6 +1437,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // A helper method to split the code point and the key code. Ultimately, they should not be // squashed into the same variable, and this method should be removed. // public for testing, as we don't want to copy the same logic into test code + @Nonnull public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int keyX, final int keyY, final boolean isKeyRepeat) { final int keyCode; @@ -1484,11 +1490,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } /** - * To be called after the InputLogic has gotten a chance to act on the on-device decoding - * for the full gesture, possibly updating the TextView to reflect the first decoding. + * To be called after the InputLogic has gotten a chance to act on the suggested words by the + * IME for the full gesture, possibly updating the TextView to reflect the first suggestion. * <p> * This method must be run on the UI Thread. - * @param suggestedWords On-device decoding for the full gesture. + * @param suggestedWords suggested words by the IME for the full gesture. */ public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) { mGestureConsumer.onImeSuggestionsProcessed(suggestedWords, @@ -1496,14 +1502,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // This method must run on the UI Thread. - private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, + void showGesturePreviewAndSuggestionStrip(@Nonnull final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { showSuggestionStrip(suggestedWords); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - mainKeyboardView.showGestureFloatingPreviewText(suggestedWords); - if (dismissGestureFloatingPreviewText) { - mainKeyboardView.dismissGestureFloatingPreviewText(); - } + mainKeyboardView.showGestureFloatingPreviewText(suggestedWords, + dismissGestureFloatingPreviewText /* dismissDelayed */); } // Called from PointerTracker through the KeyboardActionListener interface @@ -1761,7 +1765,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Hooks for hardware keyboard @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { - mSpecialKeyDetector.onKeyDown(keyEvent); + // TODO: This should be processed in {@link InputLogic}. + mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyDown(keyCode, keyEvent); } @@ -1782,7 +1787,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) { - mSpecialKeyDetector.onKeyUp(keyEvent); + // TODO: This should be processed in {@link InputLogic}. + mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyUp(keyCode, keyEvent); } @@ -1812,7 +1818,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } }; - private void launchSettings() { + void launchSettings() { mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); requestHideSelf(0); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); @@ -1836,6 +1842,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen languageSelectionTitle, getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class)) }; + final String imeId = mRichImm.getInputMethodIdOfThisIme(); final OnClickListener listener = new OnClickListener() { @Override public void onClick(DialogInterface di, int position) { @@ -1843,7 +1850,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen switch (position) { case 0: final Intent intent = IntentUtils.getInputLanguageSelectionIntent( - mRichImm.getInputMethodIdOfThisIme(), + imeId, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); diff --git a/java/src/com/android/inputmethod/latin/NgramContext.java b/java/src/com/android/inputmethod/latin/NgramContext.java index a02531cc4..82a13274d 100644 --- a/java/src/com/android/inputmethod/latin/NgramContext.java +++ b/java/src/com/android/inputmethod/latin/NgramContext.java @@ -19,26 +19,33 @@ package com.android.inputmethod.latin; import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; +import javax.annotation.Nonnull; + /** * Class to represent information of previous words. This class is used to add n-gram entries * into binary dictionaries, to get predictions, and to get suggestions. */ public class NgramContext { + @Nonnull public static final NgramContext EMPTY_PREV_WORDS_INFO = new NgramContext(WordInfo.EMPTY_WORD_INFO); + @Nonnull public static final NgramContext BEGINNING_OF_SENTENCE = - new NgramContext(WordInfo.BEGINNING_OF_SENTENCE); + new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); /** * Word information used to represent previous words information. */ public static class WordInfo { + @Nonnull public static final WordInfo EMPTY_WORD_INFO = new WordInfo(null); - public static final WordInfo BEGINNING_OF_SENTENCE = new WordInfo(); + @Nonnull + public static final WordInfo BEGINNING_OF_SENTENCE_WORD_INFO = new WordInfo(); // This is an empty char sequence when mIsBeginningOfSentence is true. public final CharSequence mWord; @@ -48,7 +55,7 @@ public class NgramContext { public final boolean mIsBeginningOfSentence; // Beginning of sentence. - public WordInfo() { + private WordInfo() { mWord = ""; mIsBeginningOfSentence = true; } @@ -96,19 +103,8 @@ public class NgramContext { mPrevWordsCount = prevWordsInfo.length; } - // Construct from WordInfo array and size. The caller shouldn't change prevWordsInfo after - // calling this method. - private NgramContext(final NgramContext ngramContext, final int prevWordsCount) { - if (ngramContext.mPrevWordsCount < prevWordsCount) { - throw new IndexOutOfBoundsException("ngramContext.mPrevWordsCount (" - + ngramContext.mPrevWordsCount + ") is smaller than prevWordsCount (" - + prevWordsCount + ")"); - } - mPrevWordsInfo = ngramContext.mPrevWordsInfo; - mPrevWordsCount = prevWordsCount; - } - // Create next prevWordsInfo using current prevWordsInfo. + @Nonnull public NgramContext getNextNgramContext(final WordInfo wordInfo) { final int nextPrevWordCount = Math.min(Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM, mPrevWordsCount + 1); diff --git a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java index 396d062f8..2dbab0a3f 100644 --- a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java @@ -26,14 +26,14 @@ import java.util.concurrent.atomic.AtomicInteger; import android.content.Context; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.latin.ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback; +import com.android.inputmethod.latin.ExpandableBinaryDictionary.UpdateEntriesForInputEventsCallback; import com.android.inputmethod.latin.personalization.PersonalizationDataChunk; import com.android.inputmethod.latin.personalization.PersonalizationDictionary; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.DistracterFilter; import com.android.inputmethod.latin.utils.DistracterFilterCheckingIsInDictionary; -import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; +import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; /** * Class for managing and updating personalization dictionaries. @@ -119,10 +119,10 @@ public class PersonalizationHelperForDictionaryFacilitator { return personalizationDict; } - private void addEntriesToPersonalizationDictionariesForLocale(final Locale locale, + private void updateEntriesOfPersonalizationDictionariesForLocale(final Locale locale, final PersonalizationDataChunk personalizationDataChunk, final SpacingAndPunctuations spacingAndPunctuations, - final AddMultipleDictionaryEntriesCallback callback) { + final UpdateEntriesForInputEventsCallback callback) { final ExpandableBinaryDictionary personalizationDict = getPersonalizationDictToUpdate(mContext, locale); if (personalizationDict == null) { @@ -131,25 +131,25 @@ public class PersonalizationHelperForDictionaryFacilitator { } return; } - final ArrayList<LanguageModelParam> languageModelParams = - LanguageModelParam.createLanguageModelParamsFrom( + final ArrayList<WordInputEventForPersonalization> inputEvents = + WordInputEventForPersonalization.createInputEventFrom( personalizationDataChunk.mTokens, personalizationDataChunk.mTimestampInSeconds, spacingAndPunctuations, locale, new DistracterFilterCheckingIsInDictionary( mDistracterFilter, personalizationDict)); - if (languageModelParams == null || languageModelParams.isEmpty()) { + if (inputEvents == null || inputEvents.isEmpty()) { if (callback != null) { callback.onFinished(); } return; } - personalizationDict.addMultipleDictionaryEntriesDynamically(languageModelParams, callback); + personalizationDict.updateEntriesForInputEvents(inputEvents, callback); } - public void addEntriesToPersonalizationDictionariesToUpdate(final Locale defaultLocale, + public void updateEntriesOfPersonalizationDictionaries(final Locale defaultLocale, final PersonalizationDataChunk personalizationDataChunk, final SpacingAndPunctuations spacingAndPunctuations, - final AddMultipleDictionaryEntriesCallback callback) { + final UpdateEntriesForInputEventsCallback callback) { final String language = personalizationDataChunk.mDetectedLanguage; final HashSet<Locale> locales; if (mIsMonolingualUser && PersonalizationDataChunk.LANGUAGE_UNKNOWN.equals(language) @@ -165,8 +165,8 @@ public class PersonalizationHelperForDictionaryFacilitator { return; } final AtomicInteger remainingTaskCount = new AtomicInteger(locales.size()); - final AddMultipleDictionaryEntriesCallback callbackForLocales = - new AddMultipleDictionaryEntriesCallback() { + final UpdateEntriesForInputEventsCallback callbackForLocales = + new UpdateEntriesForInputEventsCallback() { @Override public void onFinished() { if (remainingTaskCount.decrementAndGet() == 0) { @@ -178,7 +178,7 @@ public class PersonalizationHelperForDictionaryFacilitator { } }; for (final Locale locale : locales) { - addEntriesToPersonalizationDictionariesForLocale(locale, personalizationDataChunk, + updateEntriesOfPersonalizationDictionariesForLocale(locale, personalizationDataChunk, spacingAndPunctuations, callbackForLocales); } } diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java index 56014cbad..a65304cd0 100644 --- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java +++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java @@ -17,7 +17,8 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.internal.KeySpecParser; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import java.util.ArrayList; import java.util.Arrays; @@ -56,7 +57,7 @@ public final class PunctuationSuggestions extends SuggestedWords { /** * {@inheritDoc} - * Note that {@link super#getWord(int)} returns a punctuation key specification text. + * Note that {@link SuggestedWords#getWord(int)} returns a punctuation key specification text. * The suggested punctuation should be gotten by parsing the key specification. */ @Override @@ -70,7 +71,7 @@ public final class PunctuationSuggestions extends SuggestedWords { /** * {@inheritDoc} - * Note that {@link super#getWord(int)} returns a punctuation key specification text. + * Note that {@link SuggestedWords#getWord(int)} returns a punctuation key specification text. * The displayed text should be gotten by parsing the key specification. */ @Override @@ -82,7 +83,7 @@ public final class PunctuationSuggestions extends SuggestedWords { /** * {@inheritDoc} * Note that {@link #getWord(int)} returns a suggested punctuation. We should create a - * {@link SuggestedWordInfo} object that represents a hard coded word. + * {@link SuggestedWords.SuggestedWordInfo} object that represents a hard coded word. */ @Override public SuggestedWordInfo getInfo(final int index) { diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java index bc8bd831c..7b1a53a6e 100644 --- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -16,8 +16,8 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; @@ -50,16 +50,16 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { if (mLock.readLock().tryLock()) { try { - return mBinaryDictionary.getSuggestions(composer, ngramContext, proximityInfo, - settingsValuesForSuggestion, sessionId, weightForLocale, - inOutWeightOfLangModelVsSpatialModel); + return mBinaryDictionary.getSuggestions(composedData, ngramContext, + proximityInfoHandle, settingsValuesForSuggestion, sessionId, + weightForLocale, inOutWeightOfLangModelVsSpatialModel); } finally { mLock.readLock().unlock(); } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index a3f7bb4d6..834f747d9 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -34,6 +34,8 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import com.android.inputmethod.compat.InputConnectionCompatUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.CapsModeUtils; @@ -41,10 +43,9 @@ import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.NgramContextUtils; import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.SpannableStringUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; -import java.util.Arrays; +import javax.annotation.Nonnull; /** * Enrichment class for InputConnection to simplify interaction and add functionality. @@ -91,7 +92,7 @@ public final class RichInputConnection implements PrivateCommandPerformer { /** * This variable is a temporary object used in - * {@link #commitTextWithBackgroundColor(CharSequence, int, int)} to avoid object creation. + * {@link #commitTextWithBackgroundColor(CharSequence,int,int,int)} to avoid object creation. */ private SpannableStringBuilder mTempObjectForCommitText = new SpannableStringBuilder(); /** @@ -151,9 +152,8 @@ public final class RichInputConnection implements PrivateCommandPerformer { } else { if (DBG) { throw new RuntimeException("Nest level too deep"); - } else { - Log.e(TAG, "Nest level too deep : " + mNestLevel); } + Log.e(TAG, "Nest level too deep : " + mNestLevel); } if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); @@ -351,10 +351,9 @@ public final class RichInputConnection implements PrivateCommandPerformer { // If we have some composing text and a space before, then we should have // MODE_CHARACTERS and MODE_WORDS on. return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & inputType; - } else { - // We have some composing text - we should be in MODE_CHARACTERS only. - return TextUtils.CAP_MODE_CHARACTERS & inputType; } + // We have some composing text - we should be in MODE_CHARACTERS only. + return TextUtils.CAP_MODE_CHARACTERS & inputType; } // TODO: this will generally work, but there may be cases where the buffer contains SOME // information but not enough to determine the caps mode accurately. This may happen after @@ -369,7 +368,9 @@ public final class RichInputConnection implements PrivateCommandPerformer { } // This never calls InputConnection#getCapsMode - in fact, it's a static method that // never blocks or initiates IPC. - return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, + // TODO: don't call #toString() here. Instead, all accesses to + // mCommittedTextBeforeComposingText should be done on the main thread. + return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText.toString(), inputType, spacingAndPunctuations, hasSpaceBefore); } @@ -595,6 +596,7 @@ public final class RichInputConnection implements PrivateCommandPerformer { } @SuppressWarnings("unused") + @Nonnull public NgramContext getNgramContextFromNthPreviousWord( final SpacingAndPunctuations spacingAndPunctuations, final int n) { mIC = mParent.getCurrentInputConnection(); @@ -624,10 +626,6 @@ public final class RichInputConnection implements PrivateCommandPerformer { prev, spacingAndPunctuations, n); } - private static boolean isSeparator(final int code, final int[] sortedSeparators) { - return Arrays.binarySearch(sortedSeparators, code) >= 0; - } - private static boolean isPartOfCompositionForScript(final int codePoint, final SpacingAndPunctuations spacingAndPunctuations, final int scriptId) { // We always consider word connectors part of compositions. @@ -977,7 +975,8 @@ public final class RichInputConnection implements PrivateCommandPerformer { /** * @return {@code true} if the application reported that the monitor mode of - * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is currently enabled. + * {@link InputMethodService#onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo)} + * is currently enabled. */ public boolean isCursorAnchorInfoMonitorEnabled() { return mCursorAnchorInfoMonitorEnabled; diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index e6df35bea..8d8e7ac38 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -16,11 +16,10 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE; import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Resources; import android.os.Build; import android.os.IBinder; import android.preference.PreferenceManager; @@ -39,6 +38,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import javax.annotation.Nonnull; + /** * Enrichment class for InputMethodManager to simplify interaction and add functionality. */ @@ -302,12 +303,13 @@ public class RichInputMethodManager { return INDEX_NOT_FOUND; } + @Nonnull public InputMethodSubtype getCurrentRawSubtype() { return mImmWrapper.mImm.getCurrentInputMethodSubtype(); } public RichInputMethodSubtype createCurrentRichInputMethodSubtype( - final InputMethodSubtype rawSubtype) { + @Nonnull final InputMethodSubtype rawSubtype) { return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype, mContext); } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 6fc549549..98bce95bd 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -16,8 +16,7 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; - +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -35,6 +34,7 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -44,6 +44,8 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import javax.annotation.Nonnull; + public final class SubtypeSwitcher { private static boolean DBG = DebugFlags.DEBUG_ENABLED; private static final String TAG = SubtypeSwitcher.class.getSimpleName(); @@ -169,7 +171,7 @@ public final class SubtypeSwitcher { } // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. - public void onSubtypeChanged(final InputMethodSubtype newSubtype) { + public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) { final RichInputMethodSubtype richSubtype = mRichImm.createCurrentRichInputMethodSubtype(newSubtype); if (DBG) { @@ -291,8 +293,9 @@ public final class SubtypeSwitcher { } private static RichInputMethodSubtype sForcedSubtypeForTesting = null; + @UsedForTesting - void forceSubtype(final InputMethodSubtype subtype) { + static void forceSubtype(final InputMethodSubtype subtype) { sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index e181237a6..9b4619d35 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -20,14 +20,16 @@ import android.text.TextUtils; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SuggestionResults; import java.util.ArrayList; +import java.util.HashMap; import java.util.Locale; /** @@ -49,6 +51,16 @@ public final class Suggest { private static final boolean DBG = DebugFlags.DEBUG_ENABLED; private final DictionaryFacilitator mDictionaryFacilitator; + private static final int MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN = 12; + private static final HashMap<String, Integer> sLanguageToMaximumAutoCorrectionWithSpaceLength = + new HashMap<>(); + static { + // TODO: should we add Finnish here? + // TODO: This should not be hardcoded here but be written in the dictionary header + sLanguageToMaximumAutoCorrectionWithSpaceLength.put(Locale.GERMAN.getLanguage(), + MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN); + } + private float mAutoCorrectionThreshold; public Suggest(final DictionaryFacilitator dictionaryFacilitator) { @@ -128,8 +140,8 @@ public final class Suggest { : typedWord; final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, ngramContext, proximityInfo, settingsValuesForSuggestion, - SESSION_ID_TYPING); + wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), + settingsValuesForSuggestion, SESSION_ID_TYPING); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, trailingSingleQuotesCount, @@ -148,28 +160,50 @@ public final class Suggest { || (consideredWord.length() > 1 && !didRemoveTypedWord); final boolean hasAutoCorrection; - // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because - // any attempt to do auto-correction is already shielded with a test for this flag; at the - // same time, it feels wrong that the SuggestedWord object includes information about - // the current settings. It may also be useful to know, when the setting is off, whether - // the word *would* have been auto-corrected. - if (!isCorrectionEnabled || !allowsToBeAutoCorrected || resultsArePredictions - || suggestionResults.isEmpty() || wordComposer.hasDigits() - || wordComposer.isMostlyCaps() || wordComposer.isResumed() + // If correction is not enabled, we never auto-correct. This is for example for when + // the setting "Auto-correction" is "off": we still suggest, but we don't auto-correct. + if (!isCorrectionEnabled + // If the word does not allow to be auto-corrected, then we don't auto-correct. + || !allowsToBeAutoCorrected + // If we are doing prediction, then we never auto-correct of course + || resultsArePredictions + // If we don't have suggestion results, we can't evaluate the first suggestion + // for auto-correction + || suggestionResults.isEmpty() + // If the word has digits, we never auto-correct because it's likely the word + // was type with a lot of care + || wordComposer.hasDigits() + // If the word is mostly caps, we never auto-correct because this is almost + // certainly intentional (and careful input) + || wordComposer.isMostlyCaps() + // We never auto-correct when suggestions are resumed because it would be unexpected + || wordComposer.isResumed() + // If we don't have a main dictionary, we never want to auto-correct. The reason + // for this is, the user may have a contact whose name happens to match a valid + // word in their language, and it will unexpectedly auto-correct. For example, if + // the user types in English with no dictionary and has a "Will" in their contact + // list, "will" would always auto-correct to "Will" which is unwanted. Hence, no + // main dict => no auto-correct. Also, it would probably get obnoxious quickly. + // TODO: now that we have personalization, we may want to re-evaluate this decision || !mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary() + // If the first suggestion is a shortcut we never auto-correct to it, regardless + // of how strong it is (whitelist entries are not KIND_SHORTCUT but KIND_WHITELIST). + // TODO: we may want to have shortcut-only entries auto-correct in the future. || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { - // If we don't have a main dictionary, we never want to auto-correct. The reason for - // this is, the user may have a contact whose name happens to match a valid word in - // their language, and it will unexpectedly auto-correct. For example, if the user - // types in English with no dictionary and has a "Will" in their contact list, "will" - // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no - // auto-correct. - // Also, shortcuts should never auto-correct unless they are whitelist entries. - // TODO: we may want to have shortcut-only entries auto-correct in the future. hasAutoCorrection = false; } else { - hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( - suggestionResults.first(), consideredWord, mAutoCorrectionThreshold); + final SuggestedWordInfo firstSuggestion = suggestionResults.first(); + if (!AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( + firstSuggestion, consideredWord, mAutoCorrectionThreshold)) { + // Score is too low for autocorrect + hasAutoCorrection = false; + } else { + // We have a high score, so we need to check if this suggestion is in the correct + // form to allow auto-correcting to it in this language. For details of how this + // is determined, see #isAllowedByAutoCorrectionWithSpaceFilter. + // TODO: this should not have its own logic here but be handled by the dictionary. + hasAutoCorrection = isAllowedByAutoCorrectionWithSpaceFilter(firstSuggestion); + } } if (!TextUtils.isEmpty(typedWord)) { @@ -213,8 +247,8 @@ public final class Suggest { final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, ngramContext, proximityInfo, settingsValuesForSuggestion, - SESSION_ID_GESTURE); + wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), + settingsValuesForSuggestion, SESSION_ID_GESTURE); // For transforming words that don't come from a dictionary, because it's our best bet final Locale defaultLocale = mDictionaryFacilitator.getMostProbableLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = @@ -287,6 +321,41 @@ public final class Suggest { return suggestionsList; } + /** + * Computes whether this suggestion should be blocked or not in this language + * + * This function implements a filter that avoids auto-correcting to suggestions that contain + * spaces that are above a certain language-dependent character limit. In languages like German + * where it's possible to concatenate many words, it often happens our dictionary does not + * have the longer words. In this case, we offer a lot of unhelpful suggestions that contain + * one or several spaces. Ideally we should understand what the user wants and display useful + * suggestions by improving the dictionary and possibly having some specific logic. Until + * that's possible we should avoid displaying unhelpful suggestions. But it's hard to tell + * whether a suggestion is useful or not. So at least for the time being we block + * auto-correction when the suggestion is long and contains a space, which should avoid the + * worst damage. + * This function is implementing that filter. If the language enforces no such limit, then it + * always returns true. If the suggestion contains no space, it also returns true. Otherwise, + * it checks the length against the language-specific limit. + * + * @param info the suggestion info + * @return whether it's fine to auto-correct to this. + */ + private static boolean isAllowedByAutoCorrectionWithSpaceFilter(final SuggestedWordInfo info) { + final Locale locale = info.mSourceDict.mLocale; + if (null == locale) { + return true; + } + final Integer maximumLengthForThisLanguage = + sLanguageToMaximumAutoCorrectionWithSpaceLength.get(locale.getLanguage()); + if (null == maximumLengthForThisLanguage) { + // This language does not enforce a maximum length to auto-correction + return true; + } + return info.mWord.length() <= maximumLengthForThisLanguage + || -1 == info.mWord.indexOf(Constants.CODE_SPACE); + } + /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo( final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) { diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index e5bf25d5f..c51e20f21 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -20,13 +20,16 @@ import android.text.TextUtils; import android.view.inputmethod.CompletionInfo; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import javax.annotation.Nonnull; + public class SuggestedWords { public static final int INDEX_OF_TYPED_WORD = 0; public static final int INDEX_OF_AUTO_CORRECTION = 1; @@ -45,6 +48,7 @@ public class SuggestedWords { public static final int MAX_SUGGESTIONS = 18; private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0); + @Nonnull private static final SuggestedWords EMPTY = new SuggestedWords( EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, INPUT_STYLE_NONE); @@ -209,6 +213,7 @@ public class SuggestedWords { return result; } + @Nonnull public static final SuggestedWords getEmptyInstance() { return SuggestedWords.EMPTY; } @@ -365,9 +370,8 @@ public class SuggestedWords { public String toString() { if (TextUtils.isEmpty(mDebugString)) { return mWord; - } else { - return mWord + " (" + mDebugString + ")"; } + return mWord + " (" + mDebugString + ")"; } // This will always remove the higher index if a duplicate is found. diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index 21014b378..2b7fb1748 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -28,7 +28,7 @@ import android.provider.UserDictionary.Words; import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.compat.UserDictionaryCompatUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -64,7 +64,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { private static final String NAME = "userunigram"; private ContentObserver mObserver; - final private String mLocale; + final private String mLocaleString; final private boolean mAlsoUseMoreRestrictiveLocales; protected UserBinaryDictionary(final Context context, final Locale locale, @@ -74,9 +74,9 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { final String localeStr = locale.toString(); if (SubtypeLocaleUtils.NO_LANGUAGE.equals(localeStr)) { // If we don't have a locale, insert into the "all locales" user dictionary. - mLocale = USER_DICTIONARY_ALL_LANGUAGES; + mLocaleString = USER_DICTIONARY_ALL_LANGUAGES; } else { - mLocale = localeStr; + mLocaleString = localeStr; } mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales; ContentResolver cres = context.getContentResolver(); @@ -101,7 +101,8 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { reloadDictionaryIfRequired(); } - @UsedForTesting + // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. + @ExternallyReferenced public static UserBinaryDictionary getDictionary(final Context context, final Locale locale, final File dictFile, final String dictNamePrefix) { return new UserBinaryDictionary(context, locale, false /* alsoUseMoreRestrictiveLocales */, @@ -124,7 +125,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { // This is correct for locale processing. // For this example, we'll look at the "en_US_POSIX" case. final String[] localeElements = - TextUtils.isEmpty(mLocale) ? new String[] {} : mLocale.split("_", 3); + TextUtils.isEmpty(mLocaleString) ? new String[] {} : mLocaleString.split("_", 3); final int length = localeElements.length; final StringBuilder request = new StringBuilder("(locale is NULL)"); @@ -207,9 +208,8 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { if (client != null) { client.release(); return true; - } else { - return false; } + return false; } /** @@ -227,17 +227,16 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY, null, locale); } - private int scaleFrequencyFromDefaultToLatinIme(final int defaultFrequency) { + private static int scaleFrequencyFromDefaultToLatinIme(final int defaultFrequency) { // The default frequency for the user dictionary is 250 for historical reasons. // Latin IME considers a good value for the default user dictionary frequency // is about 160 considering the scale we use. So we are scaling down the values. if (defaultFrequency > Integer.MAX_VALUE / LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY) { return (defaultFrequency / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY) * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY; - } else { - return (defaultFrequency * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY) - / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY; } + return (defaultFrequency * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY) + / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY; } private void addWordsLocked(final Cursor cursor) { @@ -257,12 +256,14 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { runGCIfRequiredLocked(true /* mindsBlockByGC */); addUnigramLocked(word, adjustedFrequency, null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */, - false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); if (null != shortcut && shortcut.length() <= MAX_WORD_LENGTH) { runGCIfRequiredLocked(true /* mindsBlockByGC */); addUnigramLocked(shortcut, adjustedFrequency, word, USER_DICT_SHORTCUT_FREQUENCY, true /* isNotAWord */, - false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); } } cursor.moveToNext(); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 5eb338eb3..fa55319d2 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -19,9 +19,12 @@ package com.android.inputmethod.latin; import com.android.inputmethod.event.CombinerChain; import com.android.inputmethod.event.Event; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Collections; @@ -88,6 +91,10 @@ public final class WordComposer { refreshTypedWordCache(); } + public ComposedData getComposedDataSnapshot() { + return new ComposedData(getInputPointers(), isBatchMode(), mTypedWordCache.toString()); + } + /** * Restart the combiners, possibly with a new spec. * @param combiningSpec The spec string for combining. This is found in the extra value. @@ -132,38 +139,6 @@ public final class WordComposer { return mCodePointSize; } - /** - * 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( - final int[] destination) { - // This method can be called on a separate thread and mTypedWordCache can change while we - // are executing this method. - final String typedWord = mTypedWordCache.toString(); - // lastIndex is exclusive - final int lastIndex = typedWord.length() - - StringUtils.getTrailingSingleQuotesCount(typedWord); - 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(typedWord, 0, lastIndex); - if (codePointSize > destination.length) { - return -1; - } - return StringUtils.copyCodePointsAndReturnCodePointCount(destination, typedWord, 0, - lastIndex, true /* downCase */); - } - public boolean isSingleLetter() { return size() == 1; } @@ -182,7 +157,7 @@ public final class WordComposer { * @return the processed event. Never null, but may be marked as consumed. */ @Nonnull - public Event processEvent(final Event event) { + public Event processEvent(@Nonnull final Event event) { final Event processedEvent = mCombinerChain.processEvent(mEvents, event); // The retained state of the combiner chain may have changed while processing the event, // so we need to update our cache. @@ -353,9 +328,8 @@ public final class WordComposer { if (size() <= 1) { return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED; - } else { - return mCapsCount == size(); } + return mCapsCount == size(); } public boolean wasShiftedNoLock() { diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java index a87785b1a..d4be0e397 100644 --- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java +++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java @@ -44,7 +44,7 @@ import java.util.Locale; * A class to read a local file as a dictionary for debugging purposes. */ public class ExternalDictionaryGetterForDebug { - private static final String SOURCE_FOLDER = Environment.getExternalStorageDirectory().getPath() + static final String SOURCE_FOLDER = Environment.getExternalStorageDirectory().getPath() + "/Download"; private static String[] findDictionariesInTheDownloadedFolder() { @@ -142,8 +142,7 @@ public class ExternalDictionaryGetterForDebug { }).create().show(); } - private static void installFile(final Context context, final File file, - final DictionaryHeader header) { + static void installFile(final Context context, final File file, final DictionaryHeader header) { BufferedOutputStream outputStream = null; File tempFile = null; try { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 5cc61db79..bafea178e 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -39,10 +39,8 @@ import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.keyboard.TextDecorator; import com.android.inputmethod.keyboard.TextDecoratorUiOperator; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryFacilitator; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LastComposedWord; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.NgramContext; @@ -52,6 +50,9 @@ import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; @@ -61,13 +62,14 @@ import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.StatsUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; import java.util.ArrayList; import java.util.TreeSet; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + /** * This class manages the input logic. */ @@ -75,7 +77,7 @@ public final class InputLogic { private static final String TAG = InputLogic.class.getSimpleName(); // TODO : Remove this member when we can. - private final LatinIME mLatinIME; + final LatinIME mLatinIME; private final SuggestionStripViewAccessor mSuggestionStripViewAccessor; // Never null. @@ -454,8 +456,8 @@ public final class InputLogic { * {@link com.android.inputmethod.keyboard.KeyboardSwitcher#getKeyboardShiftMode()} * @return the complete transaction object */ - public InputTransaction onCodeInput(final SettingsValues settingsValues, final Event event, - final int keyboardShiftMode, + public InputTransaction onCodeInput(final SettingsValues settingsValues, + @Nonnull final Event event, final int keyboardShiftMode, // TODO: remove these arguments final int currentKeyboardScriptId, final LatinIME.UIHandler handler) { final Event processedEvent = mWordComposer.processEvent(event); @@ -1373,7 +1375,7 @@ public final class InputLogic { } private void performAdditionToUserHistoryDictionary(final SettingsValues settingsValues, - final String suggestion, final NgramContext ngramContext) { + final String suggestion, @Nonnull final NgramContext ngramContext) { // If correction is not enabled, we don't add words to the user history dictionary. // That's to avoid unintended additions in some sensitive fields, or fields that // expect to receive non-words. @@ -1512,12 +1514,6 @@ public final class InputLogic { } } final int[] codePoints = StringUtils.toCodePointArray(typedWord); - // We want the context of preceding words for suggestion. If we have chars in the word - // before the cursor, then we want the word before that, hence 2; otherwise, - // we want the word immediately before the cursor, hence 1. - final NgramContext ngramContext = getNgramContextFromNthPreviousWordForSuggestion( - settingsValues.mSpacingAndPunctuations, - 0 == numberOfCharsInWordBeforeCursor ? 1 : 2); mWordComposer.setComposingWord(codePoints, mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); mWordComposer.setCursorPositionWithinWord( @@ -1533,8 +1529,7 @@ public final class InputLogic { SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { - mIsAutoCorrectionIndicatorOn = false; - mLatinIME.mHandler.showSuggestionStrip(suggestedWords); + doShowSuggestionsAndClearAutoCorrectionIndicator(suggestedWords); }}); } else { // We found suggestion spans in the word. We'll create the SuggestedWords out of @@ -1545,11 +1540,15 @@ public final class InputLogic { null /* rawSuggestions */, typedWord, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, SuggestedWords.INPUT_STYLE_RECORRECTION, SuggestedWords.NOT_A_SEQUENCE_NUMBER); - mIsAutoCorrectionIndicatorOn = false; - mLatinIME.mHandler.showSuggestionStrip(suggestedWords); + doShowSuggestionsAndClearAutoCorrectionIndicator(suggestedWords); } } + void doShowSuggestionsAndClearAutoCorrectionIndicator(final SuggestedWords suggestedWords) { + mIsAutoCorrectionIndicatorOn = false; + mLatinIME.mHandler.showSuggestionStrip(suggestedWords); + } + /** * Reverts a previous commit with auto-correction. * @@ -1761,12 +1760,12 @@ public final class InputLogic { // word information from textview. return mConnection.getNgramContextFromNthPreviousWord( spacingAndPunctuations, nthPreviousWord); - } else { - return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? - NgramContext.BEGINNING_OF_SENTENCE : - new NgramContext(new NgramContext.WordInfo( - mLastComposedWord.mCommittedWord.toString())); } + if (LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord) { + return NgramContext.BEGINNING_OF_SENTENCE; + } + return new NgramContext(new NgramContext.WordInfo( + mLastComposedWord.mCommittedWord.toString())); } /** @@ -1819,9 +1818,8 @@ public final class InputLogic { // If no code point, #getCodePointBeforeCursor returns NOT_A_CODE_POINT. if (Constants.CODE_PERIOD == codePointBeforeCursor) { return text.substring(1); - } else { - return text; } + return text; } /** @@ -1877,7 +1875,7 @@ public final class InputLogic { * @param previousSuggestedWords The previously suggested words. * @return Obsolete suggestions with the newly typed word. */ - private SuggestedWords retrieveOlderSuggestions(final String typedWord, + static SuggestedWords retrieveOlderSuggestions(final String typedWord, final SuggestedWords previousSuggestedWords) { final SuggestedWords oldSuggestedWords = previousSuggestedWords.isPunctuationSuggestions() ? SuggestedWords.getEmptyInstance() : previousSuggestedWords; @@ -2305,7 +2303,7 @@ public final class InputLogic { * Sets the UI operator for {@link TextDecorator}. * @param uiOperator the UI operator which should be associated with {@link TextDecorator}. */ - public void setTextDecoratorUi(final TextDecoratorUiOperator uiOperator) { + public void setTextDecoratorUi(@Nonnull final TextDecoratorUiOperator uiOperator) { mTextDecorator.setUiOperator(uiOperator); } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index c6f83d0b9..ddc4ad99c 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -21,11 +21,10 @@ import android.os.HandlerThread; import android.os.Message; import com.android.inputmethod.compat.LooperCompatUtils; -import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinIME; -import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; +import com.android.inputmethod.latin.common.InputPointers; /** * A helper to manage deferred tasks for the input logic. @@ -62,7 +61,7 @@ class InputLogicHandler implements Handler.Callback { final OnGetSuggestedWordsCallback callback) {} }; - private InputLogicHandler() { + InputLogicHandler() { mNonUIThreadHandler = null; mLatinIME = null; mInputLogic = null; @@ -134,30 +133,38 @@ class InputLogicHandler implements Handler.Callback { return; } mInputLogic.mWordComposer.setBatchInputPointers(batchPointers); + final OnGetSuggestedWordsCallback callback = new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords(final SuggestedWords suggestedWords) { + showGestureSuggestionsWithPreviewVisuals(suggestedWords, isTailBatchInput); + } + }; getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH - : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, - new OnGetSuggestedWordsCallback() { - @Override - public void onGetSuggestedWords(SuggestedWords suggestedWords) { - // We're now inside the callback. This always runs on the Non-UI thread, - // no matter what thread updateBatchInput was originally called on. - if (suggestedWords.isEmpty()) { - // Use old suggestions if we don't have any new ones. - // Previous suggestions are found in InputLogic#mSuggestedWords. - // Since these are the most recent ones and we just recomputed - // new ones to update them, then the previous ones are there. - suggestedWords = mInputLogic.mSuggestedWords; - } - mLatinIME.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWords, - isTailBatchInput /* dismissGestureFloatingPreviewText */); - if (isTailBatchInput) { - mInBatchInput = false; - // The following call schedules onEndBatchInputInternal - // to be called on the UI thread. - mLatinIME.mHandler.showTailBatchInputResult(suggestedWords); - } - } - }); + : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, callback); + } + } + + void showGestureSuggestionsWithPreviewVisuals(final SuggestedWords suggestedWordsForBatchInput, + final boolean isTailBatchInput) { + final SuggestedWords suggestedWordsToShowSuggestions; + // We're now inside the callback. This always runs on the Non-UI thread, + // no matter what thread updateBatchInput was originally called on. + if (suggestedWordsForBatchInput.isEmpty()) { + // Use old suggestions if we don't have any new ones. + // Previous suggestions are found in InputLogic#mSuggestedWords. + // Since these are the most recent ones and we just recomputed + // new ones to update them, then the previous ones are there. + suggestedWordsToShowSuggestions = mInputLogic.mSuggestedWords; + } else { + suggestedWordsToShowSuggestions = suggestedWordsForBatchInput; + } + mLatinIME.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWordsToShowSuggestions, + isTailBatchInput /* dismissGestureFloatingPreviewText */); + if (isTailBatchInput) { + mInBatchInput = false; + // The following call schedules onEndBatchInputInternal + // to be called on the UI thread. + mLatinIME.mHandler.showTailBatchInputResult(suggestedWordsToShowSuggestions); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java index fa7c2c417..644818ba6 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java +++ b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java @@ -40,8 +40,9 @@ public final class DictionaryHeader { public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE"; public static final String FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY = "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID"; - public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT"; - public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT"; + public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_ENTRY_COUNT"; + public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_ENTRY_COUNT"; + public static final String MAX_TRIGRAM_COUNT_KEY = "MAX_TRIGRAM_ENTRY_COUNT"; public static final String ATTRIBUTE_VALUE_TRUE = "1"; public static final String CODE_POINT_TABLE_KEY = "codePointTable"; diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 34edfa0da..4ef504856 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -17,7 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Date; import java.util.HashMap; @@ -93,7 +93,7 @@ public final class FormatSpec { * s | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS * | is not a word ? 1 bit, 1 = yes, 0 = no : FLAG_IS_NOT_A_WORD - * | is blacklisted ? 1 bit, 1 = yes, 0 = no : FLAG_IS_BLACKLISTED + * | is possibly offensive ? 1 bit, 1 = yes, 0 = no : FLAG_IS_POSSIBLY_OFFENSIVE * * c | IF FLAG_HAS_MULTIPLE_CHARS * h | char, char, char, char n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers @@ -171,14 +171,18 @@ public final class FormatSpec { // ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType(). public static final int VERSION2 = 2; public static final int VERSION201 = 201; + public static final int VERSION202 = 202; public static final int MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE = VERSION201; // Dictionary version used for testing. public static final int VERSION4_ONLY_FOR_TESTING = 399; - public static final int VERSION401 = 401; - public static final int VERSION4 = 402; - public static final int VERSION4_DEV = 403; - static final int MINIMUM_SUPPORTED_VERSION = VERSION2; - static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV; + public static final int VERSION402 = 402; + public static final int VERSION403 = 403; + public static final int VERSION4 = VERSION403; + public static final int VERSION4_DEV = VERSION403; + static final int MINIMUM_SUPPORTED_STATIC_VERSION = VERSION202; + static final int MAXIMUM_SUPPORTED_STATIC_VERSION = VERSION202; + static final int MINIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4; + static final int MAXIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4_DEV; // TODO: Make this value adaptative to content data, store it in the header, and // use it in the reading code. @@ -197,7 +201,7 @@ public final class FormatSpec { static final int FLAG_HAS_SHORTCUT_TARGETS = 0x08; static final int FLAG_HAS_BIGRAMS = 0x04; static final int FLAG_IS_NOT_A_WORD = 0x02; - static final int FLAG_IS_BLACKLISTED = 0x01; + static final int FLAG_IS_POSSIBLY_OFFENSIVE = 0x01; static final int FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT = 0x80; static final int FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE = 0x40; diff --git a/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java b/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java index 99e0e273f..b1d19dc3c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java @@ -1,3 +1,19 @@ +/* + * 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.makedict; import com.android.inputmethod.latin.NgramContext; diff --git a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java index 5fcbb6357..03c2ece1d 100644 --- a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java +++ b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java @@ -40,11 +40,8 @@ public final class ProbabilityInfo { if (probabilityInfo2 == null) { return probabilityInfo1; } - if (probabilityInfo1.mProbability > probabilityInfo2.mProbability) { - return probabilityInfo1; - } else { - return probabilityInfo2; - } + return (probabilityInfo1.mProbability > probabilityInfo2.mProbability) ? probabilityInfo1 + : probabilityInfo2; } public ProbabilityInfo(final int probability) { @@ -67,9 +64,8 @@ public final class ProbabilityInfo { public int hashCode() { if (hasHistoricalInfo()) { return Arrays.hashCode(new Object[] { mProbability, mTimestamp, mLevel, mCount }); - } else { - return Arrays.hashCode(new Object[] { mProbability }); } + return Arrays.hashCode(new Object[] { mProbability }); } @Override diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java index 1e6cadf03..388d57816 100644 --- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -18,10 +18,11 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.CombinedFormatUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; @@ -41,7 +42,7 @@ public final class WordProperty implements Comparable<WordProperty> { // TODO: Support mIsBeginningOfSentence. public final boolean mIsBeginningOfSentence; public final boolean mIsNotAWord; - public final boolean mIsBlacklistEntry; + public final boolean mIsPossiblyOffensive; public final boolean mHasShortcuts; public final boolean mHasNgrams; @@ -52,7 +53,7 @@ public final class WordProperty implements Comparable<WordProperty> { public WordProperty(final String word, final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> shortcutTargets, @Nullable final ArrayList<WeightedString> bigrams, - final boolean isNotAWord, final boolean isBlacklistEntry) { + final boolean isNotAWord, final boolean isPossiblyOffensive) { mWord = word; mProbabilityInfo = probabilityInfo; mShortcutTargets = shortcutTargets; @@ -61,15 +62,13 @@ public final class WordProperty implements Comparable<WordProperty> { } else { mNgrams = new ArrayList<>(); final NgramContext ngramContext = new NgramContext(new WordInfo(mWord)); - if (bigrams != null) { - for (final WeightedString bigramTarget : bigrams) { - mNgrams.add(new NgramProperty(bigramTarget, ngramContext)); - } + for (final WeightedString bigramTarget : bigrams) { + mNgrams.add(new NgramProperty(bigramTarget, ngramContext)); } } mIsBeginningOfSentence = false; mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; + mIsPossiblyOffensive = isPossiblyOffensive; mHasNgrams = bigrams != null && !bigrams.isEmpty(); mHasShortcuts = shortcutTargets != null && !shortcutTargets.isEmpty(); } @@ -85,10 +84,10 @@ public final class WordProperty implements Comparable<WordProperty> { // Construct word property using information from native code. // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY. public WordProperty(final int[] codePoints, final boolean isNotAWord, - final boolean isBlacklisted, final boolean hasBigram, final boolean hasShortcuts, + final boolean isPossiblyOffensive, final boolean hasBigram, final boolean hasShortcuts, final boolean isBeginningOfSentence, final int[] probabilityInfo, final ArrayList<int[][]> ngramPrevWordsArray, - final ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray, + final ArrayList<boolean[]> ngramPrevWordIsBeginningOfSentenceArray, final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo, final ArrayList<int[]> shortcutTargets, final ArrayList<Integer> shortcutProbabilities) { @@ -98,20 +97,27 @@ public final class WordProperty implements Comparable<WordProperty> { final ArrayList<NgramProperty> ngrams = new ArrayList<>(); mIsBeginningOfSentence = isBeginningOfSentence; mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklisted; + mIsPossiblyOffensive = isPossiblyOffensive; mHasShortcuts = hasShortcuts; mHasNgrams = hasBigram; final int relatedNgramCount = ngramTargets.size(); - final WordInfo currentWordInfo = - mIsBeginningOfSentence ? WordInfo.BEGINNING_OF_SENTENCE : new WordInfo(mWord); - final NgramContext ngramContext = new NgramContext(currentWordInfo); for (int i = 0; i < relatedNgramCount; i++) { final String ngramTargetString = StringUtils.getStringFromNullTerminatedCodePointArray(ngramTargets.get(i)); final WeightedString ngramTarget = new WeightedString(ngramTargetString, createProbabilityInfoFromArray(ngramProbabilityInfo.get(i))); - // TODO: Support n-gram. + final int[][] prevWords = ngramPrevWordsArray.get(i); + final boolean[] isBeginningOfSentenceArray = + ngramPrevWordIsBeginningOfSentenceArray.get(i); + final WordInfo[] wordInfoArray = new WordInfo[prevWords.length]; + for (int j = 0; j < prevWords.length; j++) { + wordInfoArray[j] = isBeginningOfSentenceArray[j] + ? WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO + : new WordInfo(StringUtils.getStringFromNullTerminatedCodePointArray( + prevWords[j])); + } + final NgramContext ngramContext = new NgramContext(wordInfoArray); ngrams.add(new NgramProperty(ngramTarget, ngramContext)); } mNgrams = ngrams.isEmpty() ? null : ngrams; @@ -126,6 +132,7 @@ public final class WordProperty implements Comparable<WordProperty> { } // TODO: Remove + @UsedForTesting public ArrayList<WeightedString> getBigrams() { if (null == mNgrams) { return null; @@ -150,7 +157,7 @@ public final class WordProperty implements Comparable<WordProperty> { word.mShortcutTargets, word.mNgrams, word.mIsNotAWord, - word.mIsBlacklistEntry + word.mIsPossiblyOffensive }); } @@ -180,7 +187,7 @@ public final class WordProperty implements Comparable<WordProperty> { WordProperty w = (WordProperty)o; return mProbabilityInfo.equals(w.mProbabilityInfo) && mWord.equals(w.mWord) && mShortcutTargets.equals(w.mShortcutTargets) && equals(mNgrams, w.mNgrams) - && mIsNotAWord == w.mIsNotAWord && mIsBlacklistEntry == w.mIsBlacklistEntry + && mIsNotAWord == w.mIsNotAWord && mIsPossiblyOffensive == w.mIsPossiblyOffensive && mHasNgrams == w.mHasNgrams && mHasShortcuts && w.mHasNgrams; } @@ -202,7 +209,7 @@ public final class WordProperty implements Comparable<WordProperty> { @UsedForTesting public boolean isValid() { - return getProbability() != BinaryDictionary.NOT_A_PROBABILITY; + return getProbability() != Dictionary.NOT_A_PROBABILITY; } @Override diff --git a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java b/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java index e2d24fd0a..079d07eac 100644 --- a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java +++ b/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java @@ -85,12 +85,11 @@ public class BlockingHttpClient { throw new AuthException(mConnection.getResponseMessage()); } throw new HttpException(responseCode); - } else { - if (DEBUG) { - Log.d(TAG, "request executed successfully"); - } - return responseProcessor.onSuccess(mConnection.getInputStream()); } + if (DEBUG) { + Log.d(TAG, "request executed successfully"); + } + return responseProcessor.onSuccess(mConnection.getInputStream()); } finally { mConnection.disconnect(); } diff --git a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java b/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java index 502f72f17..df54bf464 100644 --- a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java +++ b/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java @@ -95,7 +95,7 @@ public class HttpUrlConnectionBuilder { } /** - * Sets the connect timeout. Defaults to {@value #DEFAULT_TIMEOUT} milliseconds. + * Sets the connect timeout. Defaults to {@value #DEFAULT_TIMEOUT_MILLIS} milliseconds. * * TODO: Remove @UsedForTesting after this method is actually used. */ @@ -110,7 +110,7 @@ public class HttpUrlConnectionBuilder { } /** - * Sets the read timeout. Defaults to {@value #DEFAULT_TIMEOUT} milliseconds. + * Sets the read timeout. Defaults to {@value #DEFAULT_TIMEOUT_MILLIS} milliseconds. * * TODO: Remove @UsedForTesting after this method is actually used. */ diff --git a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java index ac55b9333..39d9596ef 100644 --- a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; -import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.ExpandableBinaryDictionary; @@ -36,7 +36,9 @@ public class ContextualDictionary extends ExpandableBinaryDictionary { clear(); } - @UsedForTesting + // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. + @SuppressWarnings("unused") + @ExternallyReferenced public static ContextualDictionary getDictionary(final Context context, final Locale locale, final File dictFile, final String dictNamePrefix) { return new ContextualDictionary(context, locale, dictFile); diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java index 1ba7b366f..78b51d9f4 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java @@ -39,14 +39,10 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB public static final int FREQUENCY_FOR_WORDS_IN_DICTS = FREQUENCY_FOR_TYPED; public static final int FREQUENCY_FOR_WORDS_NOT_IN_DICTS = Dictionary.NOT_A_PROBABILITY; - /** The locale for this dictionary. */ - public final Locale mLocale; - protected DecayingExpandableBinaryDictionaryBase(final Context context, final String dictName, final Locale locale, final String dictionaryType, final File dictFile) { super(context, dictName, locale, dictionaryType, dictFile); - mLocale = locale; if (mLocale != null && mLocale.toString().length() > 1) { reloadDictionaryIfRequired(); } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java index f2ad22ac7..33d1273f7 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; -import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.Dictionary; import java.io.File; @@ -33,7 +33,9 @@ public class PersonalizationDictionary extends DecayingExpandableBinaryDictionar Dictionary.TYPE_PERSONALIZATION, null /* dictFile */); } - @UsedForTesting + // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. + @SuppressWarnings("unused") + @ExternallyReferenced public static PersonalizationDictionary getDictionary(final Context context, final Locale locale, final File dictFile, final String dictNamePrefix) { return PersonalizationHelper.getPersonalizationDictionary(context, locale); diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java index 59761547d..58782c646 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java @@ -17,25 +17,25 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; -import android.text.TextUtils; -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.ExpandableBinaryDictionary; import com.android.inputmethod.latin.NgramContext; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.DistracterFilter; import java.io.File; import java.util.Locale; +import javax.annotation.Nonnull; + /** * Locally gathers stats about the words user types and various other signals like auto-correction * cancellation or manual picks. This allows the keyboard to adapt to the typist over time. */ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase { /* package */ static final String NAME = UserHistoryDictionary.class.getSimpleName(); - private final static int SUPPORTED_NGRAM = 2; // TODO: 3 // TODO: Make this constructor private /* package */ UserHistoryDictionary(final Context context, final Locale locale) { @@ -43,7 +43,9 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas Dictionary.TYPE_USER_HISTORY, null /* dictFile */); } - @UsedForTesting + // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. + @SuppressWarnings("unused") + @ExternallyReferenced public static UserHistoryDictionary getDictionary(final Context context, final Locale locale, final File dictFile, final String dictNamePrefix) { return PersonalizationHelper.getUserHistoryDictionary(context, locale); @@ -60,8 +62,8 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas * @param distracterFilter the filter to check whether the word is a distracter */ public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary, - final NgramContext ngramContext, final String word, final boolean isValid, - final int timestamp, final DistracterFilter distracterFilter) { + @Nonnull final NgramContext ngramContext, final String word, final boolean isValid, + final int timestamp, @Nonnull final DistracterFilter distracterFilter) { if (word.length() > Constants.DICTIONARY_MAX_WORD_LENGTH) { return; } diff --git a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java index 3303ab093..d2c9dbbe9 100644 --- a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java @@ -113,6 +113,7 @@ public final class AdvancedSettingsFragment extends SubScreenFragment { setupKeypressVibrationDurationSettings(); setupKeypressSoundVolumeSettings(); + setupKeyLongpressTimeoutSettings(); refreshEnablingsOfKeypressSoundAndVibrationSettings(); } @@ -249,4 +250,43 @@ public final class AdvancedSettingsFragment extends SubScreenFragment { } }); } + + private void setupKeyLongpressTimeoutSettings() { + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( + Settings.PREF_KEY_LONGPRESS_TIMEOUT); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putInt(key, value).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return Settings.readKeyLongpressTimeout(prefs, res); + } + + @Override + public int readDefaultValue(final String key) { + return Settings.readDefaultKeyLongpressTimeout(res); + } + + @Override + public String getValueText(final int value) { + return res.getString(R.string.abbreviation_unit_milliseconds, value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } } diff --git a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java index a9884ba13..554edc85c 100644 --- a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java @@ -19,9 +19,9 @@ package com.android.inputmethod.latin.settings; import android.os.Bundle; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.define.ProductionFlags; - /** * "Appearance" settings sub screen. */ @@ -30,8 +30,8 @@ public final class AppearanceSettingsFragment extends SubScreenFragment { public void onCreate(final Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.prefs_screen_appearance); - if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED - || !Settings.getInstance().getCurrent().isTablet()) { + if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED || + Constants.isPhone(Settings.readScreenMetrics(getResources()))) { removePreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD); } } @@ -43,4 +43,4 @@ public final class AppearanceSettingsFragment extends SubScreenFragment { findPreference(Settings.PREF_CUSTOM_INPUT_STYLES)); ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME)); } -}
\ No newline at end of file +} diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java new file mode 100644 index 000000000..01398f467 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java @@ -0,0 +1,359 @@ +/* + * 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.settings; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.DialogPreference; +import android.preference.Preference; +import android.util.Log; +import android.view.View; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; + +import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; +import com.android.inputmethod.compat.ViewCompatUtils; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.TreeSet; + +final class CustomInputStylePreference extends DialogPreference + implements DialogInterface.OnCancelListener { + private static final boolean DEBUG_SUBTYPE_ID = false; + + interface Listener { + public void onRemoveCustomInputStyle(CustomInputStylePreference stylePref); + public void onSaveCustomInputStyle(CustomInputStylePreference stylePref); + public void onAddCustomInputStyle(CustomInputStylePreference stylePref); + public SubtypeLocaleAdapter getSubtypeLocaleAdapter(); + public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter(); + } + + private static final String KEY_PREFIX = "subtype_pref_"; + private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new"; + + private InputMethodSubtype mSubtype; + private InputMethodSubtype mPreviousSubtype; + + private final Listener mProxy; + private Spinner mSubtypeLocaleSpinner; + private Spinner mKeyboardLayoutSetSpinner; + + public static CustomInputStylePreference newIncompleteSubtypePreference( + final Context context, final Listener proxy) { + return new CustomInputStylePreference(context, null, proxy); + } + + public CustomInputStylePreference(final Context context, final InputMethodSubtype subtype, + final Listener proxy) { + super(context, null); + setDialogLayoutResource(R.layout.additional_subtype_dialog); + setPersistent(false); + mProxy = proxy; + setSubtype(subtype); + } + + public void show() { + showDialog(null); + } + + public final boolean isIncomplete() { + return mSubtype == null; + } + + public InputMethodSubtype getSubtype() { + return mSubtype; + } + + public void setSubtype(final InputMethodSubtype subtype) { + mPreviousSubtype = mSubtype; + mSubtype = subtype; + if (isIncomplete()) { + setTitle(null); + setDialogTitle(R.string.add_style); + setKey(KEY_NEW_SUBTYPE); + } else { + final String displayName = + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); + setTitle(displayName); + setDialogTitle(displayName); + setKey(KEY_PREFIX + subtype.getLocale() + "_" + + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype)); + } + } + + public void revert() { + setSubtype(mPreviousSubtype); + } + + public boolean hasBeenModified() { + return mSubtype != null && !mSubtype.equals(mPreviousSubtype); + } + + @Override + protected View onCreateDialogView() { + final View v = super.onCreateDialogView(); + mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner); + mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); + mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); + mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); + // All keyboard layout names are in the Latin script and thus left to right. That means + // the view would align them to the left even if the system locale is RTL, but that + // would look strange. To fix this, we align them to the view's start, which will be + // natural for any direction. + ViewCompatUtils.setTextAlignment( + mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START); + return v; + } + + @Override + protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) { + builder.setCancelable(true).setOnCancelListener(this); + if (isIncomplete()) { + builder.setPositiveButton(R.string.add, this) + .setNegativeButton(android.R.string.cancel, this); + } else { + builder.setPositiveButton(R.string.save, this) + .setNeutralButton(android.R.string.cancel, this) + .setNegativeButton(R.string.remove, this); + final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype); + final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype); + setSpinnerPosition(mSubtypeLocaleSpinner, localeItem); + setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem); + } + } + + private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) { + final SpinnerAdapter adapter = spinner.getAdapter(); + final int count = adapter.getCount(); + for (int i = 0; i < count; i++) { + final Object item = spinner.getItemAtPosition(i); + if (item.equals(itemToSelect)) { + spinner.setSelection(i); + return; + } + } + } + + @Override + public void onCancel(final DialogInterface dialog) { + if (isIncomplete()) { + mProxy.onRemoveCustomInputStyle(this); + } + } + + @Override + public void onClick(final DialogInterface dialog, final int which) { + super.onClick(dialog, which); + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + final boolean isEditing = !isIncomplete(); + final SubtypeLocaleItem locale = + (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem(); + final KeyboardLayoutSetItem layout = + (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem(); + final InputMethodSubtype subtype = + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + locale.mLocaleString, layout.mLayoutName); + setSubtype(subtype); + notifyChanged(); + if (isEditing) { + mProxy.onSaveCustomInputStyle(this); + } else { + mProxy.onAddCustomInputStyle(this); + } + break; + case DialogInterface.BUTTON_NEUTRAL: + // Nothing to do + break; + case DialogInterface.BUTTON_NEGATIVE: + mProxy.onRemoveCustomInputStyle(this); + break; + } + } + + private static int getSpinnerPosition(final Spinner spinner) { + if (spinner == null) return -1; + return spinner.getSelectedItemPosition(); + } + + private static void setSpinnerPosition(final Spinner spinner, final int position) { + if (spinner == null || position < 0) return; + spinner.setSelection(position); + } + + @Override + protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + final Dialog dialog = getDialog(); + if (dialog == null || !dialog.isShowing()) { + return superState; + } + + final SavedState myState = new SavedState(superState); + myState.mSubtype = mSubtype; + myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner); + myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner); + return myState; + } + + @Override + protected void onRestoreInstanceState(final Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + + final SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos); + setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos); + setSubtype(myState.mSubtype); + } + + static final class SavedState extends Preference.BaseSavedState { + InputMethodSubtype mSubtype; + int mSubtypeLocaleSelectedPos; + int mKeyboardLayoutSetSelectedPos; + + public SavedState(final Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mSubtypeLocaleSelectedPos); + dest.writeInt(mKeyboardLayoutSetSelectedPos); + dest.writeParcelable(mSubtype, 0); + } + + public SavedState(final Parcel source) { + super(source); + mSubtypeLocaleSelectedPos = source.readInt(); + mKeyboardLayoutSetSelectedPos = source.readInt(); + mSubtype = (InputMethodSubtype)source.readParcelable(null); + } + + @SuppressWarnings("hiding") + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + @Override + public SavedState createFromParcel(final Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(final int size) { + return new SavedState[size]; + } + }; + } + + static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> { + public final String mLocaleString; + private final String mDisplayName; + + public SubtypeLocaleItem(final InputMethodSubtype subtype) { + mLocaleString = subtype.getLocale(); + mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale( + mLocaleString); + } + + // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} + // to get display name. + @Override + public String toString() { + return mDisplayName; + } + + @Override + public int compareTo(final SubtypeLocaleItem o) { + return mLocaleString.compareTo(o.mLocaleString); + } + } + + static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> { + private static final String TAG_SUBTYPE = SubtypeLocaleAdapter.class.getSimpleName(); + + public SubtypeLocaleAdapter(final Context context) { + super(context, android.R.layout.simple_spinner_item); + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + final TreeSet<SubtypeLocaleItem> items = new TreeSet<>(); + final InputMethodInfo imi = RichInputMethodManager.getInstance() + .getInputMethodInfoOfThisIme(); + final int count = imi.getSubtypeCount(); + for (int i = 0; i < count; i++) { + final InputMethodSubtype subtype = imi.getSubtypeAt(i); + if (DEBUG_SUBTYPE_ID) { + Log.d(TAG_SUBTYPE, String.format("%-6s 0x%08x %11d %s", + subtype.getLocale(), subtype.hashCode(), subtype.hashCode(), + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype))); + } + if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) { + items.add(new SubtypeLocaleItem(subtype)); + } + } + // TODO: Should filter out already existing combinations of locale and layout. + addAll(items); + } + } + + static final class KeyboardLayoutSetItem { + public final String mLayoutName; + private final String mDisplayName; + + public KeyboardLayoutSetItem(final InputMethodSubtype subtype) { + mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); + mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype); + } + + // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} + // to get display name. + @Override + public String toString() { + return mDisplayName; + } + } + + static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> { + public KeyboardLayoutSetAdapter(final Context context) { + super(context, android.R.layout.simple_spinner_item); + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + // TODO: Should filter out already existing combinations of locale and layout. + for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) { + // This is a dummy subtype with NO_LANGUAGE, only for display. + final InputMethodSubtype subtype = + AdditionalSubtypeUtils.createDummyAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, layout); + add(new KeyboardLayoutSetItem(subtype)); + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java index c633fc167..46fcc7106 100644 --- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java @@ -17,16 +17,12 @@ package com.android.inputmethod.latin.settings; import android.app.AlertDialog; -import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.preference.DialogPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; @@ -39,15 +35,9 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; -import android.widget.ArrayAdapter; -import android.widget.Spinner; -import android.widget.SpinnerAdapter; import android.widget.Toast; -import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; -import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; @@ -56,19 +46,18 @@ import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.ArrayList; -import java.util.TreeSet; -public final class CustomInputStyleSettingsFragment extends PreferenceFragment { +public final class CustomInputStyleSettingsFragment extends PreferenceFragment + implements CustomInputStylePreference.Listener { private static final String TAG = CustomInputStyleSettingsFragment.class.getSimpleName(); - private static final boolean DEBUG_SUBTYPE_ID = false; // Note: We would like to turn this debug flag true in order to see what input styles are // defined in a bug-report. private static final boolean DEBUG_CUSTOM_INPUT_STYLES = true; private RichInputMethodManager mRichImm; private SharedPreferences mPrefs; - private SubtypeLocaleAdapter mSubtypeLocaleAdapter; - private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter; + private CustomInputStylePreference.SubtypeLocaleAdapter mSubtypeLocaleAdapter; + private CustomInputStylePreference.KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter; private boolean mIsAddingNewSubtype; private AlertDialog mSubtypeEnablerNotificationDialog; @@ -79,320 +68,6 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { "is_subtype_enabler_notification_dialog_open"; private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler"; - static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> { - public final String mLocaleString; - private final String mDisplayName; - - public SubtypeLocaleItem(final InputMethodSubtype subtype) { - mLocaleString = subtype.getLocale(); - mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale( - mLocaleString); - } - - // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} - // to get display name. - @Override - public String toString() { - return mDisplayName; - } - - @Override - public int compareTo(final SubtypeLocaleItem o) { - return mLocaleString.compareTo(o.mLocaleString); - } - } - - static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> { - private static final String TAG_SUBTYPE = SubtypeLocaleAdapter.class.getSimpleName(); - - public SubtypeLocaleAdapter(final Context context) { - super(context, android.R.layout.simple_spinner_item); - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - - final TreeSet<SubtypeLocaleItem> items = new TreeSet<>(); - final InputMethodInfo imi = RichInputMethodManager.getInstance() - .getInputMethodInfoOfThisIme(); - final int count = imi.getSubtypeCount(); - for (int i = 0; i < count; i++) { - final InputMethodSubtype subtype = imi.getSubtypeAt(i); - if (DEBUG_SUBTYPE_ID) { - Log.d(TAG_SUBTYPE, String.format("%-6s 0x%08x %11d %s", - subtype.getLocale(), subtype.hashCode(), subtype.hashCode(), - SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype))); - } - if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) { - items.add(new SubtypeLocaleItem(subtype)); - } - } - // TODO: Should filter out already existing combinations of locale and layout. - addAll(items); - } - } - - static final class KeyboardLayoutSetItem { - public final String mLayoutName; - private final String mDisplayName; - - public KeyboardLayoutSetItem(final InputMethodSubtype subtype) { - mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); - mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype); - } - - // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} - // to get display name. - @Override - public String toString() { - return mDisplayName; - } - } - - static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> { - public KeyboardLayoutSetAdapter(final Context context) { - super(context, android.R.layout.simple_spinner_item); - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - - // TODO: Should filter out already existing combinations of locale and layout. - for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) { - // This is a dummy subtype with NO_LANGUAGE, only for display. - final InputMethodSubtype subtype = - AdditionalSubtypeUtils.createDummyAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, layout); - add(new KeyboardLayoutSetItem(subtype)); - } - } - } - - private interface SubtypeDialogProxy { - public void onRemovePressed(SubtypePreference subtypePref); - public void onSavePressed(SubtypePreference subtypePref); - public void onAddPressed(SubtypePreference subtypePref); - public SubtypeLocaleAdapter getSubtypeLocaleAdapter(); - public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter(); - } - - static final class SubtypePreference extends DialogPreference - implements DialogInterface.OnCancelListener { - private static final String KEY_PREFIX = "subtype_pref_"; - private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new"; - - private InputMethodSubtype mSubtype; - private InputMethodSubtype mPreviousSubtype; - - private final SubtypeDialogProxy mProxy; - private Spinner mSubtypeLocaleSpinner; - private Spinner mKeyboardLayoutSetSpinner; - - public static SubtypePreference newIncompleteSubtypePreference(final Context context, - final SubtypeDialogProxy proxy) { - return new SubtypePreference(context, null, proxy); - } - - public SubtypePreference(final Context context, final InputMethodSubtype subtype, - final SubtypeDialogProxy proxy) { - super(context, null); - setDialogLayoutResource(R.layout.additional_subtype_dialog); - setPersistent(false); - mProxy = proxy; - setSubtype(subtype); - } - - public void show() { - showDialog(null); - } - - public final boolean isIncomplete() { - return mSubtype == null; - } - - public InputMethodSubtype getSubtype() { - return mSubtype; - } - - public void setSubtype(final InputMethodSubtype subtype) { - mPreviousSubtype = mSubtype; - mSubtype = subtype; - if (isIncomplete()) { - setTitle(null); - setDialogTitle(R.string.add_style); - setKey(KEY_NEW_SUBTYPE); - } else { - final String displayName = - SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); - setTitle(displayName); - setDialogTitle(displayName); - setKey(KEY_PREFIX + subtype.getLocale() + "_" - + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype)); - } - } - - public void revert() { - setSubtype(mPreviousSubtype); - } - - public boolean hasBeenModified() { - return mSubtype != null && !mSubtype.equals(mPreviousSubtype); - } - - @Override - protected View onCreateDialogView() { - final View v = super.onCreateDialogView(); - mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner); - mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); - mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); - mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); - // All keyboard layout names are in the Latin script and thus left to right. That means - // the view would align them to the left even if the system locale is RTL, but that - // would look strange. To fix this, we align them to the view's start, which will be - // natural for any direction. - ViewCompatUtils.setTextAlignment( - mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START); - return v; - } - - @Override - protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) { - builder.setCancelable(true).setOnCancelListener(this); - if (isIncomplete()) { - builder.setPositiveButton(R.string.add, this) - .setNegativeButton(android.R.string.cancel, this); - } else { - builder.setPositiveButton(R.string.save, this) - .setNeutralButton(android.R.string.cancel, this) - .setNegativeButton(R.string.remove, this); - final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype); - final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype); - setSpinnerPosition(mSubtypeLocaleSpinner, localeItem); - setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem); - } - } - - private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) { - final SpinnerAdapter adapter = spinner.getAdapter(); - final int count = adapter.getCount(); - for (int i = 0; i < count; i++) { - final Object item = spinner.getItemAtPosition(i); - if (item.equals(itemToSelect)) { - spinner.setSelection(i); - return; - } - } - } - - @Override - public void onCancel(final DialogInterface dialog) { - if (isIncomplete()) { - mProxy.onRemovePressed(this); - } - } - - @Override - public void onClick(final DialogInterface dialog, final int which) { - super.onClick(dialog, which); - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - final boolean isEditing = !isIncomplete(); - final SubtypeLocaleItem locale = - (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem(); - final KeyboardLayoutSetItem layout = - (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem(); - final InputMethodSubtype subtype = - AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - locale.mLocaleString, layout.mLayoutName); - setSubtype(subtype); - notifyChanged(); - if (isEditing) { - mProxy.onSavePressed(this); - } else { - mProxy.onAddPressed(this); - } - break; - case DialogInterface.BUTTON_NEUTRAL: - // Nothing to do - break; - case DialogInterface.BUTTON_NEGATIVE: - mProxy.onRemovePressed(this); - break; - } - } - - private static int getSpinnerPosition(final Spinner spinner) { - if (spinner == null) return -1; - return spinner.getSelectedItemPosition(); - } - - private static void setSpinnerPosition(final Spinner spinner, final int position) { - if (spinner == null || position < 0) return; - spinner.setSelection(position); - } - - @Override - protected Parcelable onSaveInstanceState() { - final Parcelable superState = super.onSaveInstanceState(); - final Dialog dialog = getDialog(); - if (dialog == null || !dialog.isShowing()) { - return superState; - } - - final SavedState myState = new SavedState(superState); - myState.mSubtype = mSubtype; - myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner); - myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner); - return myState; - } - - @Override - protected void onRestoreInstanceState(final Parcelable state) { - if (!(state instanceof SavedState)) { - super.onRestoreInstanceState(state); - return; - } - - final SavedState myState = (SavedState) state; - super.onRestoreInstanceState(myState.getSuperState()); - setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos); - setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos); - setSubtype(myState.mSubtype); - } - - static final class SavedState extends Preference.BaseSavedState { - InputMethodSubtype mSubtype; - int mSubtypeLocaleSelectedPos; - int mKeyboardLayoutSetSelectedPos; - - public SavedState(final Parcelable superState) { - super(superState); - } - - @Override - public void writeToParcel(final Parcel dest, final int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(mSubtypeLocaleSelectedPos); - dest.writeInt(mKeyboardLayoutSetSelectedPos); - dest.writeParcelable(mSubtype, 0); - } - - public SavedState(final Parcel source) { - super(source); - mSubtypeLocaleSelectedPos = source.readInt(); - mKeyboardLayoutSetSelectedPos = source.readInt(); - mSubtype = (InputMethodSubtype)source.readParcelable(null); - } - - public static final Parcelable.Creator<SavedState> CREATOR = - new Parcelable.Creator<SavedState>() { - @Override - public SavedState createFromParcel(final Parcel source) { - return new SavedState(source); - } - - @Override - public SavedState[] newArray(final int size) { - return new SavedState[size]; - } - }; - } - } - public CustomInputStyleSettingsFragment() { // Empty constructor for fragment generation. } @@ -440,8 +115,9 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { @Override public void onActivityCreated(final Bundle savedInstanceState) { final Context context = getActivity(); - mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context); - mKeyboardLayoutSetAdapter = new KeyboardLayoutSetAdapter(context); + mSubtypeLocaleAdapter = new CustomInputStylePreference.SubtypeLocaleAdapter(context); + mKeyboardLayoutSetAdapter = + new CustomInputStylePreference.KeyboardLayoutSetAdapter(context); final String prefSubtypes = Settings.readPrefAdditionalSubtypes(mPrefs, getResources()); @@ -454,7 +130,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { && savedInstanceState.containsKey(KEY_IS_ADDING_NEW_SUBTYPE); if (mIsAddingNewSubtype) { getPreferenceScreen().addPreference( - SubtypePreference.newIncompleteSubtypePreference(context, mSubtypeProxy)); + CustomInputStylePreference.newIncompleteSubtypePreference(context, this)); } super.onActivityCreated(savedInstanceState); @@ -482,62 +158,60 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { } } - private final SubtypeDialogProxy mSubtypeProxy = new SubtypeDialogProxy() { - @Override - public void onRemovePressed(final SubtypePreference subtypePref) { - mIsAddingNewSubtype = false; - final PreferenceGroup group = getPreferenceScreen(); - group.removePreference(subtypePref); + @Override + public void onRemoveCustomInputStyle(final CustomInputStylePreference stylePref) { + mIsAddingNewSubtype = false; + final PreferenceGroup group = getPreferenceScreen(); + group.removePreference(stylePref); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); + } + + @Override + public void onSaveCustomInputStyle(final CustomInputStylePreference stylePref) { + final InputMethodSubtype subtype = stylePref.getSubtype(); + if (!stylePref.hasBeenModified()) { + return; + } + if (findDuplicatedSubtype(subtype) == null) { mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); + return; } - @Override - public void onSavePressed(final SubtypePreference subtypePref) { - final InputMethodSubtype subtype = subtypePref.getSubtype(); - if (!subtypePref.hasBeenModified()) { - return; - } - if (findDuplicatedSubtype(subtype) == null) { - mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); - return; - } + // Saved subtype is duplicated. + final PreferenceGroup group = getPreferenceScreen(); + group.removePreference(stylePref); + stylePref.revert(); + group.addPreference(stylePref); + showSubtypeAlreadyExistsToast(subtype); + } - // Saved subtype is duplicated. - final PreferenceGroup group = getPreferenceScreen(); - group.removePreference(subtypePref); - subtypePref.revert(); - group.addPreference(subtypePref); - showSubtypeAlreadyExistsToast(subtype); + @Override + public void onAddCustomInputStyle(final CustomInputStylePreference stylePref) { + mIsAddingNewSubtype = false; + final InputMethodSubtype subtype = stylePref.getSubtype(); + if (findDuplicatedSubtype(subtype) == null) { + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); + mSubtypePreferenceKeyForSubtypeEnabler = stylePref.getKey(); + mSubtypeEnablerNotificationDialog = createDialog(); + mSubtypeEnablerNotificationDialog.show(); + return; } - @Override - public void onAddPressed(final SubtypePreference subtypePref) { - mIsAddingNewSubtype = false; - final InputMethodSubtype subtype = subtypePref.getSubtype(); - if (findDuplicatedSubtype(subtype) == null) { - mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); - mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); - mSubtypeEnablerNotificationDialog = createDialog(); - mSubtypeEnablerNotificationDialog.show(); - return; - } - - // Newly added subtype is duplicated. - final PreferenceGroup group = getPreferenceScreen(); - group.removePreference(subtypePref); - showSubtypeAlreadyExistsToast(subtype); - } + // Newly added subtype is duplicated. + final PreferenceGroup group = getPreferenceScreen(); + group.removePreference(stylePref); + showSubtypeAlreadyExistsToast(subtype); + } - @Override - public SubtypeLocaleAdapter getSubtypeLocaleAdapter() { - return mSubtypeLocaleAdapter; - } + @Override + public CustomInputStylePreference.SubtypeLocaleAdapter getSubtypeLocaleAdapter() { + return mSubtypeLocaleAdapter; + } - @Override - public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() { - return mKeyboardLayoutSetAdapter; - } - }; + @Override + public CustomInputStylePreference.KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() { + return mKeyboardLayoutSetAdapter; + } private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) { final Context context = getActivity(); @@ -555,6 +229,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { } private AlertDialog createDialog() { + final String imeId = mRichImm.getInputMethodIdOfThisIme(); final AlertDialog.Builder builder = new AlertDialog.Builder( DialogUtils.getPlatformDialogThemeContext(getActivity())); builder.setTitle(R.string.custom_input_styles_title) @@ -564,7 +239,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { @Override public void onClick(DialogInterface dialog, int which) { final Intent intent = IntentUtils.getInputLanguageSelectionIntent( - mRichImm.getInputMethodIdOfThisIme(), + imeId, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -584,8 +259,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { final InputMethodSubtype[] subtypesArray = AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtypes); for (final InputMethodSubtype subtype : subtypesArray) { - final SubtypePreference pref = new SubtypePreference( - context, subtype, mSubtypeProxy); + final CustomInputStylePreference pref = + new CustomInputStylePreference(context, subtype, this); group.addPreference(pref); } } @@ -596,8 +271,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { final int count = group.getPreferenceCount(); for (int i = 0; i < count; i++) { final Preference pref = group.getPreference(i); - if (pref instanceof SubtypePreference) { - final SubtypePreference subtypePref = (SubtypePreference)pref; + if (pref instanceof CustomInputStylePreference) { + final CustomInputStylePreference subtypePref = (CustomInputStylePreference)pref; // We should not save newly adding subtype to preference because it is incomplete. if (subtypePref.isIncomplete()) continue; subtypes.add(subtypePref.getSubtype()); @@ -631,8 +306,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { public boolean onOptionsItemSelected(final MenuItem item) { final int itemId = item.getItemId(); if (itemId == R.id.action_add_style) { - final SubtypePreference newSubtype = - SubtypePreference.newIncompleteSubtypePreference(getActivity(), mSubtypeProxy); + final CustomInputStylePreference newSubtype = + CustomInputStylePreference.newIncompleteSubtypePreference(getActivity(), this); getPreferenceScreen().addPreference(newSubtype); newSubtype.show(); mIsAddingNewSubtype = true; diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index df0378e1d..6fffb8e9d 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -27,11 +27,10 @@ package com.android.inputmethod.latin.settings; public final class DebugSettings { public static final String PREF_DEBUG_MODE = "debug_mode"; public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; - public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY = - "force_physical_keyboard_special_key"; public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS = "pref_has_custom_key_preview_animation_params"; - public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; + public static final String PREF_RESIZE_KEYBOARD = "pref_resize_keyboard"; + public static final String PREF_KEYBOARD_HEIGHT_SCALE = "pref_keyboard_height_scale"; public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = "pref_key_preview_dismiss_duration"; public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE = diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java index e9f8d45aa..068f56df1 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java @@ -73,7 +73,6 @@ public final class DebugSettingsFragment extends SubScreenFragment dictDumpPreferenceGroup.addPreference(pref); } final Resources res = getResources(); - setupKeyLongpressTimeoutSettings(); setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, res.getInteger(R.integer.config_key_preview_show_up_duration)); setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, @@ -90,6 +89,8 @@ public final class DebugSettingsFragment extends SubScreenFragment defaultKeyPreviewDismissEndScale); setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, defaultKeyPreviewDismissEndScale); + setupKeyboardHeight( + DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE); mServiceNeedsRestart = false; mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE); @@ -143,8 +144,7 @@ public final class DebugSettingsFragment extends SubScreenFragment mServiceNeedsRestart = true; return; } - if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH) - || key.equals(DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY)) { + if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH)) { mServiceNeedsRestart = true; return; } @@ -163,18 +163,27 @@ public final class DebugSettingsFragment extends SubScreenFragment } } - private void setupKeyLongpressTimeoutSettings() { + private void setupKeyPreviewAnimationScale(final String prefKey, final float defaultValue) { final SharedPreferences prefs = getSharedPreferences(); final Resources res = getResources(); - final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( - DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT); + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); if (pref == null) { return; } pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + private static final float PERCENTAGE_FLOAT = 100.0f; + + private float getValueFromPercentage(final int percentage) { + return percentage / PERCENTAGE_FLOAT; + } + + private int getPercentageFromValue(final float floatValue) { + return (int)(floatValue * PERCENTAGE_FLOAT); + } + @Override public void writeValue(final int value, final String key) { - prefs.edit().putInt(key, value).apply(); + prefs.edit().putFloat(key, getValueFromPercentage(value)).apply(); } @Override @@ -184,17 +193,21 @@ public final class DebugSettingsFragment extends SubScreenFragment @Override public int readValue(final String key) { - return Settings.readKeyLongpressTimeout(prefs, res); + return getPercentageFromValue( + Settings.readKeyPreviewAnimationScale(prefs, key, defaultValue)); } @Override public int readDefaultValue(final String key) { - return Settings.readDefaultKeyLongpressTimeout(res); + return getPercentageFromValue(defaultValue); } @Override public String getValueText(final int value) { - return res.getString(R.string.abbreviation_unit_milliseconds, value); + if (value < 0) { + return res.getString(R.string.settings_system_default); + } + return String.format(Locale.ROOT, "%d%%", value); } @Override @@ -202,7 +215,7 @@ public final class DebugSettingsFragment extends SubScreenFragment }); } - private void setupKeyPreviewAnimationScale(final String prefKey, final float defaultValue) { + private void setupKeyPreviewAnimationDuration(final String prefKey, final int defaultValue) { final SharedPreferences prefs = getSharedPreferences(); final Resources res = getResources(); final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); @@ -210,19 +223,9 @@ public final class DebugSettingsFragment extends SubScreenFragment return; } pref.setInterface(new SeekBarDialogPreference.ValueProxy() { - private static final float PERCENTAGE_FLOAT = 100.0f; - - private float getValueFromPercentage(final int percentage) { - return percentage / PERCENTAGE_FLOAT; - } - - private int getPercentageFromValue(final float floatValue) { - return (int)(floatValue * PERCENTAGE_FLOAT); - } - @Override public void writeValue(final int value, final String key) { - prefs.edit().putFloat(key, getValueFromPercentage(value)).apply(); + prefs.edit().putInt(key, value).apply(); } @Override @@ -232,21 +235,17 @@ public final class DebugSettingsFragment extends SubScreenFragment @Override public int readValue(final String key) { - return getPercentageFromValue( - Settings.readKeyPreviewAnimationScale(prefs, key, defaultValue)); + return Settings.readKeyPreviewAnimationDuration(prefs, key, defaultValue); } @Override public int readDefaultValue(final String key) { - return getPercentageFromValue(defaultValue); + return defaultValue; } @Override public String getValueText(final int value) { - if (value < 0) { - return res.getString(R.string.settings_system_default); - } - return String.format(Locale.ROOT, "%d%%", value); + return res.getString(R.string.abbreviation_unit_milliseconds, value); } @Override @@ -254,17 +253,25 @@ public final class DebugSettingsFragment extends SubScreenFragment }); } - private void setupKeyPreviewAnimationDuration(final String prefKey, final int defaultValue) { + private void setupKeyboardHeight(final String prefKey, final float defaultValue) { final SharedPreferences prefs = getSharedPreferences(); - final Resources res = getResources(); final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); if (pref == null) { return; } pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + private static final float PERCENTAGE_FLOAT = 100.0f; + private float getValueFromPercentage(final int percentage) { + return percentage / PERCENTAGE_FLOAT; + } + + private int getPercentageFromValue(final float floatValue) { + return (int)(floatValue * PERCENTAGE_FLOAT); + } + @Override public void writeValue(final int value, final String key) { - prefs.edit().putInt(key, value).apply(); + prefs.edit().putFloat(key, getValueFromPercentage(value)).apply(); } @Override @@ -274,17 +281,18 @@ public final class DebugSettingsFragment extends SubScreenFragment @Override public int readValue(final String key) { - return Settings.readKeyPreviewAnimationDuration(prefs, key, defaultValue); + return getPercentageFromValue( + Settings.readKeyboardHeight(prefs, key, defaultValue)); } @Override public int readDefaultValue(final String key) { - return defaultValue; + return getPercentageFromValue(defaultValue); } @Override public String getValueText(final int value) { - return res.getString(R.string.abbreviation_unit_milliseconds, value); + return String.format(Locale.ROOT, "%d%%", value); } @Override diff --git a/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java index 832fbf65a..22b0655b4 100644 --- a/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java @@ -16,7 +16,6 @@ package com.android.inputmethod.latin.settings; -import android.content.SharedPreferences; import android.os.Bundle; import com.android.inputmethod.latin.R; diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java index c17110471..5c416ab18 100644 --- a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java +++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java @@ -46,15 +46,15 @@ public class LocalSettingsConstants { // correctly set for it to work on a new device. DebugSettings.PREF_DEBUG_MODE, DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, - DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY, DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, - DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT, + DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + DebugSettings.PREF_RESIZE_KEYBOARD, DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW }; diff --git a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java index b71f8829b..c5930db1e 100644 --- a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java @@ -20,8 +20,6 @@ import android.os.Bundle; import com.android.inputmethod.latin.R; -import java.util.ArrayList; - /** * "Multilingual options" settings sub screen. * diff --git a/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java b/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java index c173d4706..91444604d 100644 --- a/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java +++ b/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java @@ -43,9 +43,7 @@ public class RadioButtonPreference extends Preference { private final View.OnClickListener mClickListener = new View.OnClickListener() { @Override public void onClick(final View v) { - if (mListener != null) { - mListener.onRadioButtonClicked(RadioButtonPreference.this); - } + callListenerOnRadioButtonClicked(); } }; @@ -67,6 +65,12 @@ public class RadioButtonPreference extends Preference { mListener = listener; } + void callListenerOnRadioButtonClicked() { + if (mListener != null) { + mListener.onRadioButtonClicked(this); + } + } + @Override protected void onBindView(final View view) { super.onBindView(view); diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 103033c16..16c053474 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -29,17 +29,19 @@ import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.StatsUtils; -import com.android.inputmethod.latin.utils.StringUtils; import java.util.Collections; import java.util.Locale; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; +import javax.annotation.Nonnull; + public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = Settings.class.getSimpleName(); // Settings screens @@ -78,7 +80,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS = BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT; public static final boolean SHOULD_SHOW_LXX_SUGGESTION_UI = - BuildCompatUtils.EFFECTIVE_SDK_INT >= BuildCompatUtils.VERSION_CODES_LXX; + BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.LOLLIPOP; public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY = "pref_show_language_switch_key"; public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST = @@ -93,8 +95,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_GESTURE_INPUT = "gesture_input"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; - public static final String PREF_KEYPRESS_SOUND_VOLUME = - "pref_keypress_sound_volume"; + public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume"; + public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; + public static final String PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY = + "pref_enable_emoji_alt_physical_key"; public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail"; public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = "pref_gesture_floating_preview_text"; @@ -175,7 +179,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang } public void loadSettings(final Context context, final Locale locale, - final InputAttributes inputAttributes) { + @Nonnull final InputAttributes inputAttributes) { mSettingsValuesLock.lock(); mContext = context; try { @@ -209,6 +213,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return mSettingsValues.mBlockPotentiallyOffensive; } + public static int readScreenMetrics(final Resources res) { + return res.getInteger(R.integer.config_screen_metrics); + } + // Accessed from the settings interface, hence public public static boolean readKeypressSoundEnabled(final SharedPreferences prefs, final Resources res) { @@ -317,7 +325,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static int readKeyLongpressTimeout(final SharedPreferences prefs, final Resources res) { final int milliseconds = prefs.getInt( - DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); + PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : readDefaultKeyLongpressTimeout(res); } @@ -355,6 +363,12 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue; } + public static float readKeyboardHeight(final SharedPreferences prefs, + final String prefKey, final float defaultValue) { + final float percentage = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT); + return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue; + } + public static boolean readUseFullscreenMode(final Resources res) { return res.getBoolean(R.bool.config_use_fullscreen_mode); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 660b4e095..509b41fd3 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -25,7 +25,6 @@ import android.util.Log; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.AppWorkaroundsUtils; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; @@ -37,6 +36,8 @@ import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; import java.util.Arrays; import java.util.Locale; +import javax.annotation.Nonnull; + /** * When you call the constructor of this class, you may want to change the current system locale by * using {@link com.android.inputmethod.latin.utils.RunInLocale}. @@ -49,6 +50,7 @@ public class SettingsValues { private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue"; private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity"; private static final int TIMEOUT_TO_GET_TARGET_PACKAGE = 5; // seconds + public static final float DEFAULT_SIZE_SCALE = 1.0f; // 100% // From resources: public final SpacingAndPunctuations mSpacingAndPunctuations; @@ -78,6 +80,7 @@ public class SettingsValues { public final boolean mSlidingKeyInputPreviewEnabled; public final boolean mPhraseGestureEnabled; public final int mKeyLongpressTimeout; + public final boolean mEnableEmojiAltPhysicalKey; public final boolean mEnableMetricsLogging; public final boolean mShouldShowLxxSuggestionUi; // Use split layout for keyboard. @@ -85,6 +88,7 @@ public class SettingsValues { public final int mScreenMetrics; // From the input box + @Nonnull public final InputAttributes mInputAttributes; // Deduced settings @@ -107,6 +111,8 @@ public class SettingsValues { // Debug settings public final boolean mIsInternal; public final boolean mHasCustomKeyPreviewAnimationParams; + public final boolean mHasKeyboardResize; + public final float mKeyboardHeightScale; public final int mKeyPreviewShowUpDuration; public final int mKeyPreviewDismissDuration; public final float mKeyPreviewShowUpStartXScale; @@ -115,7 +121,7 @@ public class SettingsValues { public final float mKeyPreviewDismissEndYScale; public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, - final InputAttributes inputAttributes) { + @Nonnull final InputAttributes inputAttributes) { mLocale = res.getConfiguration().locale; // Get the resources mDelayInMillisecondsToUpdateOldSuggestions = @@ -123,12 +129,7 @@ public class SettingsValues { mSpacingAndPunctuations = new SpacingAndPunctuations(res); // Store the input attributes - if (null == inputAttributes) { - mInputAttributes = new InputAttributes( - null, false /* isFullscreenMode */, context.getPackageName()); - } else { - mInputAttributes = inputAttributes; - } + mInputAttributes = inputAttributes; // Get the settings preferences mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); @@ -159,7 +160,7 @@ public class SettingsValues { mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration()); mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true); mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false); - mScreenMetrics = res.getInteger(R.integer.config_screen_metrics); + mScreenMetrics = Settings.readScreenMetrics(res); mShouldShowLxxSuggestionUi = Settings.SHOULD_SHOW_LXX_SUGGESTION_UI && prefs.getBoolean(DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, true); @@ -168,6 +169,8 @@ public class SettingsValues { mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res); mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res); + mEnableEmojiAltPhysicalKey = prefs.getBoolean( + Settings.PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY, true); mAutoCorrectionThreshold = readAutoCorrectionThreshold(res, autoCorrectionThresholdRawValue); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); @@ -185,6 +188,9 @@ public class SettingsValues { mIsInternal = Settings.isInternal(prefs); mHasCustomKeyPreviewAnimationParams = prefs.getBoolean( DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false); + mHasKeyboardResize = prefs.getBoolean(DebugSettings.PREF_RESIZE_KEYBOARD, false); + mKeyboardHeightScale = Settings.readKeyboardHeight( + prefs, DebugSettings.PREF_KEYBOARD_HEIGHT_SCALE, DEFAULT_SIZE_SCALE); mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, res.getInteger(R.integer.config_key_preview_show_up_duration)); @@ -223,11 +229,6 @@ public class SettingsValues { return mEnableMetricsLogging; } - public boolean isTablet() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET; - } - public boolean isApplicationSpecifiedCompletionsOn() { return mInputAttributes.mApplicationSpecifiedCompletionOn; } @@ -273,9 +274,8 @@ public class SettingsValues { final RichInputMethodManager imm = RichInputMethodManager.getInstance(); if (mIncludesOtherImesInLanguageSwitchList) { return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */); - } else { - return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */); } + return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */); } public boolean isSameInputType(final EditorInfo editorInfo) { diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java index 97aad3b6d..70d97a5ba 100644 --- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java +++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java @@ -20,10 +20,10 @@ import android.content.res.Resources; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.internal.MoreKeySpec; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.PunctuationSuggestions; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 2a4e14ca7..bcf7bbfdc 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -50,13 +50,10 @@ import java.util.concurrent.Semaphore; */ public final class AndroidSpellCheckerService extends SpellCheckerService implements SharedPreferences.OnSharedPreferenceChangeListener { - private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); - private static final boolean DBG = false; - public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts"; private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480; - private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 368; + private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 301; private static final String DICTIONARY_NAME_PREFIX = "spellcheck_"; @@ -171,7 +168,8 @@ public final class AndroidSpellCheckerService extends SpellCheckerService DictionaryFacilitator dictionaryFacilitatorForLocale = mDictionaryFacilitatorCache.get(locale); return dictionaryFacilitatorForLocale.getSuggestionResults(composer, ngramContext, - proximityInfo, mSettingsValuesForSuggestion, sessionId); + proximityInfo.getNativeProximityInfo(), mSettingsValuesForSuggestion, + sessionId); } finally { if (sessionId != null) { mSessionIdPool.add(sessionId); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java index 8393b306c..2c690aea7 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -16,8 +16,10 @@ package com.android.inputmethod.latin.spellcheck; +import android.annotation.TargetApi; import android.content.res.Resources; import android.os.Binder; +import android.os.Build; import android.text.TextUtils; import android.util.Log; import android.view.textservice.SentenceSuggestionsInfo; @@ -26,7 +28,7 @@ import android.view.textservice.TextInfo; import com.android.inputmethod.compat.TextInfoCompatUtils; import com.android.inputmethod.latin.NgramContext; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.SpannableStringUtils; import java.util.ArrayList; import java.util.Locale; @@ -42,6 +44,7 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck mResources = service.getResources(); } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti, SentenceSuggestionsInfo ssi) { final CharSequence typedText = TextInfoCompatUtils.getCharSequenceOrString(ti); @@ -68,9 +71,10 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck if (!subText.toString().contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { continue; } - final CharSequence[] splitTexts = StringUtils.split(subText, + // Split preserving spans. + final CharSequence[] splitTexts = SpannableStringUtils.split(subText, AndroidSpellCheckerService.SINGLE_QUOTE, - true /* preserveTrailingEmptySegments */ ); + true /* preserveTrailingEmptySegments */); if (splitTexts == null || splitTexts.length <= 1) { continue; } @@ -149,7 +153,7 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck * @param textInfos an array of the text metadata * @param suggestionsLimit the maximum number of suggestions to be returned * @return an array of {@link SentenceSuggestionsInfo} returned by - * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} + * {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} */ private SentenceSuggestionsInfo[] splitAndSuggest(TextInfo[] textInfos, int suggestionsLimit) { if (textInfos == null || textInfos.length == 0) { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 7b6aacd15..3ad8fb910 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -30,15 +30,15 @@ import android.view.textservice.TextInfo; import com.android.inputmethod.compat.SuggestionsInfoCompatUtils; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.ScriptUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SuggestionResults; import java.util.ArrayList; @@ -312,11 +312,10 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { // Don't kill the keyboard if there is a bug in the spell checker if (DBG) { throw e; - } else { - Log.e(TAG, "Exception while spellcheking", e); - return AndroidSpellCheckerService.getNotInDictEmptySuggestions( - false /* reportAsTypo */); } + Log.e(TAG, "Exception while spellcheking", e); + return AndroidSpellCheckerService.getNotInDictEmptySuggestions( + false /* reportAsTypo */); } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java index 9ddee8629..10c458c7d 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java @@ -16,13 +16,15 @@ package com.android.inputmethod.latin.spellcheck; +import android.annotation.TargetApi; import android.content.res.Resources; +import android.os.Build; import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import com.android.inputmethod.compat.TextInfoCompatUtils; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.RunInLocale; @@ -76,19 +78,19 @@ public class SentenceLevelAdapter { private static class WordIterator { private final SpacingAndPunctuations mSpacingAndPunctuations; public WordIterator(final Resources res, final Locale locale) { - final RunInLocale<SpacingAndPunctuations> job - = new RunInLocale<SpacingAndPunctuations>() { + final RunInLocale<SpacingAndPunctuations> job = + new RunInLocale<SpacingAndPunctuations>() { @Override - protected SpacingAndPunctuations job(final Resources res) { - return new SpacingAndPunctuations(res); + protected SpacingAndPunctuations job(final Resources r) { + return new SpacingAndPunctuations(r); } }; mSpacingAndPunctuations = job.runInLocale(res, locale); } - public int getEndOfWord(final CharSequence sequence, int index) { + public int getEndOfWord(final CharSequence sequence, final int fromIndex) { final int length = sequence.length(); - index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1); + int index = fromIndex < 0 ? 0 : Character.offsetByCodePoints(sequence, fromIndex, 1); while (index < length) { final int codePoint = Character.codePointAt(sequence, index); if (mSpacingAndPunctuations.isWordSeparator(codePoint)) { @@ -111,12 +113,12 @@ public class SentenceLevelAdapter { return index; } - public int getBeginningOfNextWord(final CharSequence sequence, int index) { + public int getBeginningOfNextWord(final CharSequence sequence, final int fromIndex) { final int length = sequence.length(); - if (index >= length) { + if (fromIndex >= length) { return -1; } - index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1); + int index = fromIndex < 0 ? 0 : Character.offsetByCodePoints(sequence, fromIndex, 1); while (index < length) { final int codePoint = Character.codePointAt(sequence, index); if (!mSpacingAndPunctuations.isWordSeparator(codePoint)) { @@ -140,7 +142,7 @@ public class SentenceLevelAdapter { final int cookie = originalTextInfo.getCookie(); final int start = -1; final int end = originalText.length(); - final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>(); + final ArrayList<SentenceWordItem> wordItems = new ArrayList<>(); int wordStart = wordIterator.getBeginningOfNextWord(originalText, start); int wordEnd = wordIterator.getEndOfWord(originalText, wordStart); while (wordStart <= end && wordEnd != -1 && wordStart != -1) { @@ -158,6 +160,7 @@ public class SentenceLevelAdapter { return new SentenceTextInfoParams(originalTextInfo, wordItems); } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public static SentenceSuggestionsInfo reconstructSuggestions( SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) { if (results == null || results.length == 0) { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java index df9a76119..294666b8b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java @@ -18,7 +18,9 @@ package com.android.inputmethod.latin.spellcheck; import com.android.inputmethod.latin.utils.FragmentUtils; +import android.annotation.TargetApi; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceActivity; @@ -41,8 +43,8 @@ public final class SpellCheckerSettingsActivity extends PreferenceActivity { return modIntent; } - // TODO: Uncomment the override annotation once we start using SDK version 19. - // @Override + @TargetApi(Build.VERSION_CODES.KITKAT) + @Override public boolean isValidFragment(String fragmentName) { return FragmentUtils.isValidFragment(fragmentName); } diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 9d186d44d..37ab2669b 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -26,9 +26,9 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreSuggestions extends Keyboard { diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 7b66bbb75..27a0f62ff 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -50,10 +50,8 @@ import com.android.inputmethod.latin.PunctuationSuggestions; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; -import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; @@ -380,6 +378,7 @@ final class SuggestionStripLayoutHelper { final int countInStrip = mSuggestionsCountInStrip; mMoreSuggestionsAvailable = (wordCountToShow > countInStrip); + @SuppressWarnings("unused") int x = 0; for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) { if (positionInStrip != 0) { diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 789d549d7..b71bd1f50 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -43,10 +43,10 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; @@ -344,12 +344,6 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (mSuggestedWords.size() <= mStartIndexOfMoreSuggestions) { return false; } - // Dismiss another {@link MoreKeysPanel} that may be being showed, for example - // {@link MoreKeysKeyboardView}. - mMainKeyboardView.onDismissMoreKeysPanel(); - // Dismiss all key previews and sliding key input preview that may be being showed. - mMainKeyboardView.dismissAllKeyPreviews(); - mMainKeyboardView.dismissSlidingKeyInputPreview(); final int stripWidth = getWidth(); final View container = mMoreSuggestionsContainer; final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java index 624783a70..90e4faafd 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java @@ -47,12 +47,12 @@ public class UserDictionaryList extends PreferenceFragment { "android.settings.USER_DICTIONARY_SETTINGS"; @Override - public void onCreate(Bundle icicle) { + public void onCreate(final Bundle icicle) { super.onCreate(icicle); setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getActivity())); } - public static TreeSet<String> getUserDictionaryLocalesSet(Activity activity) { + public static TreeSet<String> getUserDictionaryLocalesSet(final Activity activity) { final Cursor cursor = activity.getContentResolver().query(UserDictionary.Words.CONTENT_URI, new String[] { UserDictionary.Words.LOCALE }, null, null, null); @@ -108,7 +108,7 @@ public class UserDictionaryList extends PreferenceFragment { * Creates the entries that allow the user to go into the user dictionary for each locale. * @param userDictGroup The group to put the settings in. */ - protected void createUserDictSettings(PreferenceGroup userDictGroup) { + protected void createUserDictSettings(final PreferenceGroup userDictGroup) { final Activity activity = getActivity(); userDictGroup.removeAll(); final TreeSet<String> localeSet = @@ -121,10 +121,10 @@ public class UserDictionaryList extends PreferenceFragment { } if (localeSet.isEmpty()) { - userDictGroup.addPreference(createUserDictionaryPreference(null, activity)); + userDictGroup.addPreference(createUserDictionaryPreference(null)); } else { for (String locale : localeSet) { - userDictGroup.addPreference(createUserDictionaryPreference(locale, activity)); + userDictGroup.addPreference(createUserDictionaryPreference(locale)); } } } @@ -134,7 +134,7 @@ public class UserDictionaryList extends PreferenceFragment { * @param locale The locale for which this user dictionary is for. * @return The corresponding preference. */ - protected Preference createUserDictionaryPreference(String locale, Activity activity) { + protected Preference createUserDictionaryPreference(final String locale) { final Preference newPref = new Preference(getActivity()); final Intent intent = new Intent(USER_DICTIONARY_SETTINGS_INTENT_ACTION); if (null == locale) { diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java index cf2014a1a..1d7e7d683 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java @@ -177,17 +177,16 @@ public class UserDictionarySettings extends ListFragment { return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, QUERY_SELECTION_ALL_LOCALES, null, "UPPER(" + UserDictionary.Words.WORD + ")"); - } else { - final String queryLocale = null != locale ? locale : Locale.getDefault().toString(); - return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, - QUERY_SELECTION, new String[] { queryLocale }, - "UPPER(" + UserDictionary.Words.WORD + ")"); } + final String queryLocale = null != locale ? locale : Locale.getDefault().toString(); + return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, + QUERY_SELECTION, new String[] { queryLocale }, + "UPPER(" + UserDictionary.Words.WORD + ")"); } private ListAdapter createAdapter() { return new MyAdapter(getActivity(), R.layout.user_dictionary_item, mCursor, - ADAPTER_FROM, ADAPTER_TO, this); + ADAPTER_FROM, ADAPTER_TO); } @Override @@ -283,13 +282,12 @@ public class UserDictionarySettings extends ListFragment { } private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer { - private AlphabetIndexer mIndexer; private ViewBinder mViewBinder = new ViewBinder() { @Override - public boolean setViewValue(View v, Cursor c, int columnIndex) { + public boolean setViewValue(final View v, final Cursor c, final int columnIndex) { if (!IS_SHORTCUT_API_SUPPORTED) { // just let SimpleCursorAdapter set the view values return false; @@ -310,10 +308,9 @@ public class UserDictionarySettings extends ListFragment { } }; - @SuppressWarnings("deprecation") - public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to, - UserDictionarySettings settings) { - super(context, layout, c, from, to); + public MyAdapter(final Context context, final int layout, final Cursor c, + final String[] from, final int[] to) { + super(context, layout, c, from, to, 0 /* flags */); if (null != c) { final String alphabet = context.getString(R.string.user_dict_fast_scroll_alphabet); @@ -324,12 +321,12 @@ public class UserDictionarySettings extends ListFragment { } @Override - public int getPositionForSection(int section) { + public int getPositionForSection(final int section) { return null == mIndexer ? 0 : mIndexer.getPositionForSection(section); } @Override - public int getSectionForPosition(int position) { + public int getSectionForPosition(final int position) { return null == mIndexer ? 0 : mIndexer.getSectionForPosition(position); } diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index db7f2a56c..2aac7c57a 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -16,12 +16,12 @@ package com.android.inputmethod.latin.utils; -import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; +import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.ASCII_CAPABLE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; import android.os.Build; import android.text.TextUtils; @@ -31,6 +31,7 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.StringUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java index d12aad639..952ac2a62 100644 --- a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java +++ b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java @@ -59,11 +59,7 @@ public class AsyncResultHolder<E> { */ public E get(final E defaultValue, final long timeOut) { try { - if (mLatch.await(timeOut, TimeUnit.MILLISECONDS)) { - return mResult; - } else { - return defaultValue; - } + return mLatch.await(timeOut, TimeUnit.MILLISECONDS) ? mResult : defaultValue; } catch (InterruptedException e) { return defaultValue; } diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java index cba769521..120cffbde 100644 --- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java @@ -24,7 +24,6 @@ import com.android.inputmethod.latin.define.DebugFlags; public final class AutoCorrectionUtils { private static final boolean DBG = DebugFlags.DEBUG_ENABLED; private static final String TAG = AutoCorrectionUtils.class.getSimpleName(); - private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; private AutoCorrectionUtils() { // Purely static class: can't instantiate. diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java index ce25fe6a4..23ffde2a2 100644 --- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.personalization.PersonalizationHelper; diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java index 02f1c5f00..0dbc7c858 100644 --- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java @@ -19,10 +19,12 @@ package com.android.inputmethod.latin.utils; import android.text.InputType; import android.text.TextUtils; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; +import java.util.ArrayList; import java.util.Locale; public final class CapsModeUtils { @@ -325,4 +327,31 @@ public final class CapsModeUtils { // Here we arrived at the start of the line. This should behave exactly like whitespace. return (START == state || LETTER == state) ? noCaps : caps; } + + /** + * Convert capitalize mode flags into human readable text. + * + * @param capsFlags The modes flags to be converted. It may be any combination of + * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and + * {@link TextUtils#CAP_MODE_SENTENCES}. + * @return the text that describe the <code>capsMode</code>. + */ + public static String flagsToString(final int capsFlags) { + final int capsFlagsMask = TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS + | TextUtils.CAP_MODE_SENTENCES; + if ((capsFlags & ~capsFlagsMask) != 0) { + return "unknown<0x" + Integer.toHexString(capsFlags) + ">"; + } + final ArrayList<String> builder = new ArrayList<>(); + if ((capsFlags & android.text.TextUtils.CAP_MODE_CHARACTERS) != 0) { + builder.add("characters"); + } + if ((capsFlags & android.text.TextUtils.CAP_MODE_WORDS) != 0) { + builder.add("words"); + } + if ((capsFlags & android.text.TextUtils.CAP_MODE_SENTENCES) != 0) { + builder.add("sentences"); + } + return builder.isEmpty() ? "none" : TextUtils.join("|", builder); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java index fb36b7c50..01f5e1079 100644 --- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java @@ -18,21 +18,31 @@ package com.android.inputmethod.latin.utils; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; +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. } - public static <E> ArrayList<E> arrayAsList(final E[] array, final int start, final int end) { - if (array == null) { - throw new NullPointerException(); - } + /** + * 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(); + throw new IllegalArgumentException("Invalid start: " + start + " end: " + end + + " with array.length: " + array.length); } final ArrayList<E> list = new ArrayList<>(end - start); @@ -47,7 +57,7 @@ public final class CollectionUtils { * @param c Collection to test. * @return Whether c contains no elements. */ - public static boolean isNullOrEmpty(final Collection c) { + public static boolean isNullOrEmpty(@Nullable final Collection<?> c) { return c == null || c.isEmpty(); } } diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java index 7e8e55990..8699f2ce7 100644 --- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.NgramProperty; import com.android.inputmethod.latin.makedict.ProbabilityInfo; import com.android.inputmethod.latin.makedict.WeightedString; import com.android.inputmethod.latin.makedict.WordProperty; @@ -26,6 +27,8 @@ import java.util.HashMap; public class CombinedFormatUtils { public static final String DICTIONARY_TAG = "dictionary"; public static final String BIGRAM_TAG = "bigram"; + public static final String NGRAM_TAG = "ngram"; + public static final String NGRAM_PREV_WORD_TAG = "prev_word"; public static final String SHORTCUT_TAG = "shortcut"; public static final String PROBABILITY_TAG = "f"; public static final String HISTORICAL_INFO_TAG = "historicalInfo"; @@ -33,7 +36,8 @@ public class CombinedFormatUtils { public static final String WORD_TAG = "word"; public static final String BEGINNING_OF_SENTENCE_TAG = "beginning_of_sentence"; public static final String NOT_A_WORD_TAG = "not_a_word"; - public static final String BLACKLISTED_TAG = "blacklisted"; + public static final String POSSIBLY_OFFENSIVE_TAG = "possibly_offensive"; + public static final String TRUE_VALUE = "true"; public static String formatAttributeMap(final HashMap<String, String> attributeMap) { final StringBuilder builder = new StringBuilder(); @@ -58,13 +62,13 @@ public class CombinedFormatUtils { builder.append(","); builder.append(formatProbabilityInfo(wordProperty.mProbabilityInfo)); if (wordProperty.mIsBeginningOfSentence) { - builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=true"); + builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=" + TRUE_VALUE); } if (wordProperty.mIsNotAWord) { - builder.append("," + NOT_A_WORD_TAG + "=true"); + builder.append("," + NOT_A_WORD_TAG + "=" + TRUE_VALUE); } - if (wordProperty.mIsBlacklistEntry) { - builder.append("," + BLACKLISTED_TAG + "=true"); + if (wordProperty.mIsPossiblyOffensive) { + builder.append("," + POSSIBLY_OFFENSIVE_TAG + "=" + TRUE_VALUE); } builder.append("\n"); if (wordProperty.mHasShortcuts) { @@ -76,12 +80,19 @@ public class CombinedFormatUtils { } } if (wordProperty.mHasNgrams) { - // TODO: Support ngram. - for (final WeightedString bigram : wordProperty.getBigrams()) { - builder.append(" " + BIGRAM_TAG + "=" + bigram.mWord); + for (final NgramProperty ngramProperty : wordProperty.mNgrams) { + builder.append(" " + NGRAM_TAG + "=" + ngramProperty.mTargetWord.mWord); builder.append(","); - builder.append(formatProbabilityInfo(bigram.mProbabilityInfo)); + builder.append(formatProbabilityInfo(ngramProperty.mTargetWord.mProbabilityInfo)); builder.append("\n"); + for (int i = 0; i < ngramProperty.mNgramContext.getPrevWordCount(); i++) { + builder.append(" " + NGRAM_PREV_WORD_TAG + "[" + i + "]=" + + ngramProperty.mNgramContext.getNthPrevWord(i + 1)); + if (ngramProperty.mNgramContext.isNthPrevWordBeginningOfSontence(i + 1)) { + builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=true"); + } + builder.append("\n"); + } } } return builder.toString(); @@ -101,4 +112,8 @@ public class CombinedFormatUtils { } return builder.toString(); } + + public static boolean isLiteralTrue(final String value) { + return TRUE_VALUE.equalsIgnoreCase(value); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java b/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java index 87df013a6..3a9705904 100644 --- a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java @@ -16,7 +16,7 @@ package com.android.inputmethod.latin.utils; -import java.util.Arrays; +import javax.annotation.Nonnull; public final class CoordinateUtils { private static final int INDEX_X = 0; @@ -27,32 +27,35 @@ public final class CoordinateUtils { // This utility class is not publicly instantiable. } + @Nonnull public static int[] newInstance() { return new int[ELEMENT_SIZE]; } - public static int x(final int[] coords) { + public static int x(@Nonnull final int[] coords) { return coords[INDEX_X]; } - public static int y(final int[] coords) { + public static int y(@Nonnull final int[] coords) { return coords[INDEX_Y]; } - public static void set(final int[] coords, final int x, final int 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(final int[] destination, final int[] source) { + 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]; @@ -62,30 +65,30 @@ public final class CoordinateUtils { return result; } - public static int xFromArray(final int[] coordsArray, final int index) { + public static int xFromArray(@Nonnull final int[] coordsArray, final int index) { return coordsArray[ELEMENT_SIZE * index + INDEX_X]; } - public static int yFromArray(final int[] coordsArray, final int index) { + public static int yFromArray(@Nonnull final int[] coordsArray, final int index) { return coordsArray[ELEMENT_SIZE * index + INDEX_Y]; } - public static int[] coordinateFromArray(final int[] coordsArray, final int index) { - final int baseIndex = ELEMENT_SIZE * index; - return Arrays.copyOfRange(coordsArray, baseIndex, baseIndex + ELEMENT_SIZE); + @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(final int[] coordsArray, final int index, + 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(final int[] coordsArray, final int index, - final int[] coords) { - final int baseIndex = ELEMENT_SIZE * index; - coordsArray[baseIndex + INDEX_X] = coords[INDEX_X]; - coordsArray[baseIndex + INDEX_Y] = coords[INDEX_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/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java index e05618901..c90d30c42 100644 --- a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java @@ -24,6 +24,7 @@ import android.inputmethodservice.InputMethodService; import android.os.Build; import android.text.Layout; import android.text.Spannable; +import android.text.Spanned; import android.view.View; import android.view.ViewParent; import android.view.inputmethod.CursorAnchorInfo; @@ -95,7 +96,7 @@ public final class CursorAnchorInfoUtils { @Nullable public static CursorAnchorInfoCompatWrapper extractFromTextView( @Nonnull final TextView textView) { - if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return null; } return CursorAnchorInfoCompatWrapper.wrap(extractFromTextViewInternal(textView)); @@ -107,7 +108,7 @@ public final class CursorAnchorInfoUtils { * @return the {@link CursorAnchorInfo} object based on the current layout. {@code null} if it * is not feasible. */ - @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Nullable private static CursorAnchorInfo extractFromTextViewInternal(@Nonnull final TextView textView) { final Layout layout = textView.getLayout(); @@ -149,7 +150,7 @@ public final class CursorAnchorInfoUtils { final Object[] spans = spannable.getSpans(0, text.length(), Object.class); for (Object span : spans) { final int spanFlag = spannable.getSpanFlags(span); - if ((spanFlag & Spannable.SPAN_COMPOSING) != 0) { + if ((spanFlag & Spanned.SPAN_COMPOSING) != 0) { composingTextStart = Math.min(composingTextStart, spannable.getSpanStart(span)); composingTextEnd = Math.max(composingTextEnd, spannable.getSpanEnd(span)); diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index e29aabacd..24025b272 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -26,8 +26,8 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.AssetFileAddress; import com.android.inputmethod.latin.BinaryDictionaryGetter; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java index 355d00dac..525212c96 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java @@ -16,14 +16,16 @@ package com.android.inputmethod.latin.utils; -import java.util.List; -import java.util.Locale; - import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.NgramContext; +import java.util.List; +import java.util.Locale; + +import javax.annotation.Nonnull; + public interface DistracterFilter { /** * Determine whether a word is a distracter to words in dictionaries. @@ -68,8 +70,9 @@ public interface DistracterFilter { public static boolean shouldBeHandledAsOov(final int handlingType) { return (handlingType & SHOULD_BE_HANDLED_AS_OOV) != 0; } - }; + } + @Nonnull public static final DistracterFilter EMPTY_DISTRACTER_FILTER = new DistracterFilter() { @Override public boolean isDistracterToWordsInDictionaries(NgramContext ngramContext, diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java index 8f0f9bb44..9c6a94810 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -40,6 +40,7 @@ import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; /** @@ -249,8 +250,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr composer.setComposingWord(codePoints, coordinates); final SuggestionResults suggestionResults; synchronized (mLock) { - suggestionResults = dictionaryFacilitator.getSuggestionResults( - composer, NgramContext.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo(), + suggestionResults = dictionaryFacilitator.getSuggestionResults(composer, + NgramContext.EMPTY_PREV_WORDS_INFO, + keyboard.getProximityInfo().getNativeProximityInfo(), settingsValuesForSuggestion, 0 /* sessionId */); } if (suggestionResults.isEmpty()) { diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java index df6e97028..4c99fed9f 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java @@ -41,10 +41,9 @@ public class DistracterFilterCheckingIsInDictionary implements DistracterFilter // This filter treats entries that are already in the dictionary as non-distracters // because they have passed the filtering in the past. return false; - } else { - return mDistracterFilter.isDistracterToWordsInDictionaries( - ngramContext, testedWord, locale); } + return mDistracterFilter.isDistracterToWordsInDictionaries( + ngramContext, testedWord, locale); } @Override diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java index 61da1b789..e77f6fd40 100644 --- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java @@ -27,7 +27,7 @@ import java.util.concurrent.ThreadFactory; * Utilities to manage executors. */ public class ExecutorUtils { - private static final ConcurrentHashMap<String, ExecutorService> sExecutorMap = + static final ConcurrentHashMap<String, ExecutorService> sExecutorMap = new ConcurrentHashMap<>(); private static class ThreadFactoryWithId implements ThreadFactory { @@ -49,7 +49,7 @@ public class ExecutorUtils { public static ExecutorService getExecutor(final String id) { ExecutorService executor = sExecutorMap.get(id); if (executor == null) { - synchronized(sExecutorMap) { + synchronized (sExecutorMap) { executor = sExecutorMap.get(id); if (executor == null) { executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithId(id)); @@ -65,7 +65,7 @@ public class ExecutorUtils { */ @UsedForTesting public static void shutdownAllExecutors() { - synchronized(sExecutorMap) { + synchronized (sExecutorMap) { for (final ExecutorService executor : sExecutorMap.values()) { executor.execute(new Runnable() { @Override diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java deleted file mode 100644 index 73aefb821..000000000 --- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.utils; - -import android.util.Log; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.DictionaryFacilitator; -import com.android.inputmethod.latin.NgramContext; -import com.android.inputmethod.latin.settings.SpacingAndPunctuations; -import com.android.inputmethod.latin.utils.DistracterFilter.HandlingType; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -// Note: this class is used as a parameter type of a native method. You should be careful when you -// rename this class or field name. See BinaryDictionary#addMultipleDictionaryEntriesNative(). -public final class LanguageModelParam { - private static final String TAG = LanguageModelParam.class.getSimpleName(); - private static final boolean DEBUG = false; - private static final boolean DEBUG_TOKEN = false; - - // For now, these probability values are being referred to only when we add new entries to - // decaying dynamic binary dictionaries. When these are referred to, what matters is 0 or - // non-0. Thus, it's not meaningful to compare 10, 100, and so on. - // TODO: Revise the logic in ForgettingCurveUtils in native code. - private static final int UNIGRAM_PROBABILITY_FOR_VALID_WORD = 100; - private static final int UNIGRAM_PROBABILITY_FOR_OOV_WORD = Dictionary.NOT_A_PROBABILITY; - private static final int BIGRAM_PROBABILITY_FOR_VALID_WORD = 10; - private static final int BIGRAM_PROBABILITY_FOR_OOV_WORD = Dictionary.NOT_A_PROBABILITY; - - public final CharSequence mTargetWord; - public final int[] mWord0; - public final int[] mWord1; - // TODO: this needs to be a list of shortcuts - public final int[] mShortcutTarget; - public final int mUnigramProbability; - public final int mBigramProbability; - public final int mShortcutProbability; - public final boolean mIsNotAWord; - public final boolean mIsBlacklisted; - // Time stamp in seconds. - public final int mTimestamp; - - // Constructor for unigram. TODO: support shortcuts - @UsedForTesting - public LanguageModelParam(final CharSequence word, final int unigramProbability, - final int timestamp) { - this(null /* word0 */, word, unigramProbability, Dictionary.NOT_A_PROBABILITY, timestamp); - } - - // Constructor for unigram and bigram. - @UsedForTesting - public LanguageModelParam(final CharSequence word0, final CharSequence word1, - final int unigramProbability, final int bigramProbability, - final int timestamp) { - mTargetWord = word1; - mWord0 = (word0 == null) ? null : StringUtils.toCodePointArray(word0); - mWord1 = StringUtils.toCodePointArray(word1); - mShortcutTarget = null; - mUnigramProbability = unigramProbability; - mBigramProbability = bigramProbability; - mShortcutProbability = Dictionary.NOT_A_PROBABILITY; - mIsNotAWord = false; - mIsBlacklisted = false; - mTimestamp = timestamp; - } - - // Process a list of words and return a list of {@link LanguageModelParam} objects. - public static ArrayList<LanguageModelParam> createLanguageModelParamsFrom( - final List<String> tokens, final int timestamp, - final SpacingAndPunctuations spacingAndPunctuations, final Locale locale, - final DistracterFilter distracterFilter) { - final ArrayList<LanguageModelParam> languageModelParams = new ArrayList<>(); - final int N = tokens.size(); - NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; - for (int i = 0; i < N; ++i) { - final String tempWord = tokens.get(i); - if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) { - // just skip this token - if (DEBUG_TOKEN) { - Log.d(TAG, "--- isEmptyStringOrWhiteSpaces: \"" + tempWord + "\""); - } - continue; - } - if (!DictionaryInfoUtils.looksValidForDictionaryInsertion( - tempWord, spacingAndPunctuations)) { - if (DEBUG_TOKEN) { - Log.d(TAG, "--- not looksValidForDictionaryInsertion: \"" - + tempWord + "\""); - } - // Sentence terminator found. Split. - ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; - continue; - } - if (DEBUG_TOKEN) { - Log.d(TAG, "--- word: \"" + tempWord + "\""); - } - final LanguageModelParam languageModelParam = - detectWhetherVaildWordOrNotAndGetLanguageModelParam( - ngramContext, tempWord, timestamp, locale, distracterFilter); - if (languageModelParam == null) { - continue; - } - languageModelParams.add(languageModelParam); - ngramContext = ngramContext.getNextNgramContext( - new NgramContext.WordInfo(tempWord)); - } - return languageModelParams; - } - - private static LanguageModelParam detectWhetherVaildWordOrNotAndGetLanguageModelParam( - final NgramContext ngramContext, final String targetWord, final int timestamp, - final Locale locale, final DistracterFilter distracterFilter) { - if (locale == null) { - return null; - } - final int wordHandlingType = distracterFilter.getWordHandlingType(ngramContext, - targetWord, locale); - final String word = HandlingType.shouldBeLowerCased(wordHandlingType) ? - targetWord.toLowerCase(locale) : targetWord; - if (distracterFilter.isDistracterToWordsInDictionaries(ngramContext, targetWord, locale)) { - // The word is a distracter. - return null; - } - return createAndGetLanguageModelParamOfWord(ngramContext, word, timestamp, - !HandlingType.shouldBeHandledAsOov(wordHandlingType)); - } - - private static LanguageModelParam createAndGetLanguageModelParamOfWord( - final NgramContext ngramContext, final String word, final int timestamp, - final boolean isValidWord) { - final int unigramProbability = isValidWord ? - UNIGRAM_PROBABILITY_FOR_VALID_WORD : UNIGRAM_PROBABILITY_FOR_OOV_WORD; - if (!ngramContext.isValid()) { - if (DEBUG) { - Log.d(TAG, "--- add unigram: current(" - + (isValidWord ? "Valid" : "OOV") + ") = " + word); - } - return new LanguageModelParam(word, unigramProbability, timestamp); - } - if (DEBUG) { - Log.d(TAG, "--- add bigram: prev = " + ngramContext + ", current(" - + (isValidWord ? "Valid" : "OOV") + ") = " + word); - } - final int bigramProbability = isValidWord ? - BIGRAM_PROBABILITY_FOR_VALID_WORD : BIGRAM_PROBABILITY_FOR_OOV_WORD; - return new LanguageModelParam(ngramContext.getNthPrevWord(1 /* n */), word, - unigramProbability, bigramProbability, timestamp); - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java b/java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java index 34eeac2c2..7d2ddd268 100644 --- a/java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/NgramContextUtils.java @@ -16,14 +16,16 @@ package com.android.inputmethod.latin.utils; -import java.util.Arrays; -import java.util.regex.Pattern; - -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; +import java.util.Arrays; +import java.util.regex.Pattern; + +import javax.annotation.Nonnull; + public final class NgramContextUtils { private NgramContextUtils() { // Intentional empty constructor for utility class. @@ -52,6 +54,7 @@ public final class NgramContextUtils { // (n = 2) "abc|" -> beginning-of-sentence // (n = 2) "abc |" -> beginning-of-sentence // (n = 2) "abc. def|" -> beginning-of-sentence + @Nonnull public static NgramContext getNgramContextFromNthPreviousWord(final CharSequence prev, final SpacingAndPunctuations spacingAndPunctuations, final int n) { if (prev == null) return NgramContext.EMPTY_PREV_WORDS_INFO; @@ -74,20 +77,20 @@ public final class NgramContextUtils { } // If we can't find (n + i) words, the context is beginning-of-sentence. if (focusedWordIndex < 0) { - prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE; + prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO; break; } final String focusedWord = w[focusedWordIndex]; // If the word is, the context is beginning-of-sentence. final int length = focusedWord.length(); if (length <= 0) { - prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE; + prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO; break; } // If ends in a sentence separator, the context is beginning-of-sentence. final char lastChar = focusedWord.charAt(length - 1); if (spacingAndPunctuations.isSentenceSeparator(lastChar)) { - prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE; + prevWordsInfo[i] = WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO; break; } // If ends in a word separator or connector, the context is unclear. diff --git a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java index e3cac97f0..a381649a4 100644 --- a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java +++ b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin.utils; +import com.android.inputmethod.latin.common.StringUtils; + import java.util.Locale; /** @@ -49,6 +51,17 @@ public class RecapitalizeStatus { } } + public static String modeToString(final int recapitalizeMode) { + switch (recapitalizeMode) { + case NOT_A_RECAPITALIZE_MODE: return "undefined"; + case CAPS_MODE_ORIGINAL_MIXED_CASE: return "mixedCase"; + case CAPS_MODE_ALL_LOWER: return "allLower"; + case CAPS_MODE_FIRST_WORD_UPPER: return "firstWordUpper"; + case CAPS_MODE_ALL_UPPER: return "allUpper"; + default: return "unknown<" + recapitalizeMode + ">"; + } + } + /** * We store the location of the cursor and the string that was there before the recapitalize * action was done, and the location of the cursor and the string that was there after. diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java index 093c5a6c1..cc0d470df 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java @@ -26,6 +26,7 @@ import android.util.TypedValue; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.settings.SettingsValues; import java.util.ArrayList; import java.util.HashMap; @@ -110,7 +111,6 @@ public final class ResourceUtils { * are true for the specified key value pairs. * * For example, "condition,constant" has the following format. - * (See {@link ResourceUtilsTests#testFindConstantForKeyValuePairsRegexp()}) * - HARDWARE=mako,constantForNexus4 * - MODEL=Nexus 4:MANUFACTURER=LGE,constantForNexus4 * - ,defaultConstant @@ -119,6 +119,7 @@ public final class ResourceUtils { * @param conditionConstantArray an array of "condition,constant" elements to be searched. * @return the constant part of the matched "condition,constant" element. Returns null if no * condition matches. + * @see com.android.inputmethod.latin.utils.ResourceUtilsTests#testFindConstantForKeyValuePairsRegexp() */ @UsedForTesting static String findConstantForKeyValuePairs(final HashMap<String, String> keyValuePairs, @@ -186,6 +187,15 @@ public final class ResourceUtils { return dm.widthPixels; } + public static int getKeyboardHeight(final Resources res, final SettingsValues settingsValues) { + final int defaultKeyboardHeight = getDefaultKeyboardHeight(res); + if (settingsValues.mHasKeyboardResize) { + // mKeyboardHeightScale Ranges from [.5,1.2], from xml/prefs_screen_debug.xml + return (int)(defaultKeyboardHeight * settingsValues.mKeyboardHeightScale); + } + return defaultKeyboardHeight; + } + public static int getDefaultKeyboardHeight(final Resources res) { final DisplayMetrics dm = res.getDisplayMetrics(); final String keyboardHeightInDp = getDeviceOverrideValue( diff --git a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java index 38164cb36..c41817fe6 100644 --- a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java @@ -24,6 +24,12 @@ import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.text.style.URLSpan; +import com.android.inputmethod.annotations.UsedForTesting; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public final class SpannableStringUtils { /** * Copies the spans from the region <code>start...end</code> in @@ -51,7 +57,7 @@ public final class SpannableStringUtils { // of a word. But the spans have been split into two by the getText{Before,After}Cursor // methods, so after concatenation they may end in the middle of a word. // Since we don't use them, we can just remove them and avoid crashing. - fl &= ~Spannable.SPAN_PARAGRAPH; + fl &= ~Spanned.SPAN_PARAGRAPH; int st = source.getSpanStart(spans[i]); int en = source.getSpanEnd(spans[i]); @@ -125,4 +131,53 @@ public final class SpannableStringUtils { final URLSpan[] spans = spanned.getSpans(startIndex - 1, endIndex + 1, URLSpan.class); return null != spans && spans.length > 0; } + + /** + * Splits the given {@code charSequence} with at occurrences of the given {@code regex}. + * <p> + * This is equivalent to + * {@code charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0)} + * except that the spans are preserved in the result array. + * </p> + * @param charSequence the character sequence to be split. + * @param regex the regex pattern to be used as the separator. + * @param preserveTrailingEmptySegments {@code true} to preserve the trailing empty + * segments. Otherwise, trailing empty segments will be removed before being returned. + * @return the array which contains the result. All the spans in the <code>charSequence</code> + * is preserved. + */ + @UsedForTesting + public static CharSequence[] split(final CharSequence charSequence, final String regex, + final boolean preserveTrailingEmptySegments) { + // A short-cut for non-spanned strings. + if (!(charSequence instanceof Spanned)) { + // -1 means that trailing empty segments will be preserved. + return charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0); + } + + // Hereafter, emulate String.split for CharSequence. + final ArrayList<CharSequence> sequences = new ArrayList<>(); + final Matcher matcher = Pattern.compile(regex).matcher(charSequence); + int nextStart = 0; + boolean matched = false; + while (matcher.find()) { + sequences.add(charSequence.subSequence(nextStart, matcher.start())); + nextStart = matcher.end(); + matched = true; + } + if (!matched) { + // never matched. preserveTrailingEmptySegments is ignored in this case. + return new CharSequence[] { charSequence }; + } + sequences.add(charSequence.subSequence(nextStart, charSequence.length())); + if (!preserveTrailingEmptySegments) { + for (int i = sequences.size() - 1; i >= 0; --i) { + if (!TextUtils.isEmpty(sequences.get(i))) { + break; + } + sequences.remove(i); + } + } + return sequences.toArray(new CharSequence[sequences.size()]); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 61661cd52..55c1dc9e5 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -16,8 +16,9 @@ package com.android.inputmethod.latin.utils; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.COMBINING_RULES; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; import android.content.Context; import android.content.res.Resources; @@ -25,13 +26,12 @@ import android.os.Build; import android.util.Log; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.Locale; /** @@ -39,10 +39,10 @@ import java.util.Locale; */ // TODO: consolidate this into RichInputMethodSubtype public final class SubtypeLocaleUtils { - private static final String TAG = SubtypeLocaleUtils.class.getSimpleName(); + static final String TAG = SubtypeLocaleUtils.class.getSimpleName(); - // This reference class {@link Constants} must be located in the same package as LatinIME.java. - private static final String RESOURCE_PACKAGE_NAME = Constants.class.getPackage().getName(); + // This reference class {@link R} must be located in the same package as LatinIME.java. + private static final String RESOURCE_PACKAGE_NAME = R.class.getPackage().getName(); // Special language code to represent "no language". public static final String NO_LANGUAGE = "zz"; @@ -59,7 +59,8 @@ public final class SubtypeLocaleUtils { // Keyboard layout to subtype name resource id map. private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = new HashMap<>(); // Exceptional locale whose name should be displayed in Locale.ROOT. - static final HashSet<String> sExceptionalLocaleDisplayedInRootLocale = new HashSet<>(); + private static final HashMap<String, Integer> sExceptionalLocaleDisplayedInRootLocale = + new HashMap<>(); // Exceptional locale to subtype name resource id map. private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = new HashMap<>(); // Exceptional locale to subtype name with layout resource id map. @@ -73,6 +74,8 @@ public final class SubtypeLocaleUtils { "string/subtype_with_layout_"; private static final String SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX = "string/subtype_no_language_"; + private static final String SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX = + "string/subtype_in_root_locale_"; // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value. // This is for compatibility to keep the same subtype ids as pre-JellyBean. private static final HashMap<String, String> sLocaleAndExtraValueToKeyboardLayoutSetMap = @@ -117,7 +120,10 @@ public final class SubtypeLocaleUtils { final String[] exceptionalLocaleInRootLocale = res.getStringArray( R.array.subtype_locale_displayed_in_root_locale); for (int i = 0; i < exceptionalLocaleInRootLocale.length; i++) { - sExceptionalLocaleDisplayedInRootLocale.add(exceptionalLocaleInRootLocale[i]); + final String localeString = exceptionalLocaleInRootLocale[i]; + final String resourceName = SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX + localeString; + final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME); + sExceptionalLocaleDisplayedInRootLocale.put(localeString, resId); } final String[] exceptionalLocales = res.getStringArray( @@ -171,7 +177,7 @@ public final class SubtypeLocaleUtils { if (NO_LANGUAGE.equals(localeString)) { return sResources.getConfiguration().locale; } - if (sExceptionalLocaleDisplayedInRootLocale.contains(localeString)) { + if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) { return Locale.ROOT; } return LocaleUtils.constructLocaleFromString(localeString); @@ -190,7 +196,7 @@ public final class SubtypeLocaleUtils { public static String getSubtypeLanguageDisplayName(final String localeString) { final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString); final String languageString; - if (sExceptionalLocaleDisplayedInRootLocale.contains(localeString)) { + if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) { languageString = localeString; } else { final Locale locale = LocaleUtils.constructLocaleFromString(localeString); @@ -205,7 +211,16 @@ public final class SubtypeLocaleUtils { // No language subtype should be displayed in system locale. return sResources.getString(R.string.subtype_no_language); } - final Integer exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString); + final Integer exceptionalNameResId; + if (displayLocale.equals(Locale.ROOT) + && sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) { + exceptionalNameResId = sExceptionalLocaleDisplayedInRootLocale.get(localeString); + } else if (sExceptionalLocaleToNameIdsMap.containsKey(localeString)) { + exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString); + } else { + exceptionalNameResId = null; + } + final String displayName; if (exceptionalNameResId != null) { final RunInLocale<String> getExceptionalName = new RunInLocale<String>() { @@ -245,9 +260,8 @@ public final class SubtypeLocaleUtils { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) { return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME); - } else { - return getSubtypeLocaleDisplayNameInternal(subtype.getLocale(), displayLocale); } + return getSubtypeLocaleDisplayNameInternal(subtype.getLocale(), displayLocale); } public static String getSubtypeDisplayNameInSystemLocale(final InputMethodSubtype subtype) { @@ -342,6 +356,6 @@ public final class SubtypeLocaleUtils { } public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) { - return subtype.getExtraValueOf(Constants.Subtype.ExtraValue.COMBINING_RULES); + return subtype.getExtraValueOf(COMBINING_RULES); } } diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java index 4e2e396c2..b319aeb8a 100644 --- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java +++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java @@ -66,8 +66,7 @@ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { return super.addAll(e); } - private static final class SuggestedWordInfoComparator - implements Comparator<SuggestedWordInfo> { + static final class SuggestedWordInfoComparator implements Comparator<SuggestedWordInfo> { // This comparator ranks the word info with the higher frequency first. That's because // that's the order we want our elements in. @Override diff --git a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java index dd122b634..0bcba2754 100644 --- a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java @@ -57,7 +57,7 @@ public final class ViewLayoutUtils { public static void updateLayoutHeightOf(final Window window, final int layoutHeight) { final WindowManager.LayoutParams params = window.getAttributes(); - if (params.height != layoutHeight) { + if (params != null && params.height != layoutHeight) { params.height = layoutHeight; window.setAttributes(params); } @@ -65,7 +65,7 @@ public final class ViewLayoutUtils { public static void updateLayoutHeightOf(final View view, final int layoutHeight) { final ViewGroup.LayoutParams params = view.getLayoutParams(); - if (params.height != layoutHeight) { + if (params != null && params.height != layoutHeight) { params.height = layoutHeight; view.setLayoutParams(params); } diff --git a/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java b/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java new file mode 100644 index 000000000..86a5b19ec --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java @@ -0,0 +1,118 @@ +/* + * 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.utils; + +import android.util.Log; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.NgramContext; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; +import com.android.inputmethod.latin.utils.DistracterFilter.HandlingType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +// Note: this class is used as a parameter type of a native method. You should be careful when you +// rename this class or field name. See BinaryDictionary#addMultipleDictionaryEntriesNative(). +public final class WordInputEventForPersonalization { + private static final String TAG = WordInputEventForPersonalization.class.getSimpleName(); + private static final boolean DEBUG_TOKEN = false; + + public final int[] mTargetWord; + public final int mPrevWordsCount; + public final int[][] mPrevWordArray = new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][]; + public final boolean[] mIsPrevWordBeginningOfSentenceArray = + new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + public final boolean mIsValid; + // Time stamp in seconds. + public final int mTimestamp; + + @UsedForTesting + public WordInputEventForPersonalization(final CharSequence targetWord, + final NgramContext ngramContext, final boolean isValid, final int timestamp) { + mTargetWord = StringUtils.toCodePointArray(targetWord); + mPrevWordsCount = ngramContext.getPrevWordCount(); + ngramContext.outputToArray(mPrevWordArray, mIsPrevWordBeginningOfSentenceArray); + mIsValid = isValid; + mTimestamp = timestamp; + } + + // Process a list of words and return a list of {@link WordInputEventForPersonalization} + // objects. + public static ArrayList<WordInputEventForPersonalization> createInputEventFrom( + final List<String> tokens, final int timestamp, + final SpacingAndPunctuations spacingAndPunctuations, final Locale locale, + final DistracterFilter distracterFilter) { + final ArrayList<WordInputEventForPersonalization> inputEvents = new ArrayList<>(); + final int N = tokens.size(); + NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; + for (int i = 0; i < N; ++i) { + final String tempWord = tokens.get(i); + if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) { + // just skip this token + if (DEBUG_TOKEN) { + Log.d(TAG, "--- isEmptyStringOrWhiteSpaces: \"" + tempWord + "\""); + } + continue; + } + if (!DictionaryInfoUtils.looksValidForDictionaryInsertion( + tempWord, spacingAndPunctuations)) { + if (DEBUG_TOKEN) { + Log.d(TAG, "--- not looksValidForDictionaryInsertion: \"" + + tempWord + "\""); + } + // Sentence terminator found. Split. + // TODO: Detect whether the context is beginning-of-sentence. + ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; + continue; + } + if (DEBUG_TOKEN) { + Log.d(TAG, "--- word: \"" + tempWord + "\""); + } + final WordInputEventForPersonalization inputEvent = + detectWhetherVaildWordOrNotAndGetInputEvent( + ngramContext, tempWord, timestamp, locale, distracterFilter); + if (inputEvent == null) { + continue; + } + inputEvents.add(inputEvent); + ngramContext = ngramContext.getNextNgramContext(new NgramContext.WordInfo(tempWord)); + } + return inputEvents; + } + + private static WordInputEventForPersonalization detectWhetherVaildWordOrNotAndGetInputEvent( + final NgramContext ngramContext, final String targetWord, final int timestamp, + final Locale locale, final DistracterFilter distracterFilter) { + if (locale == null) { + return null; + } + final int wordHandlingType = distracterFilter.getWordHandlingType(ngramContext, + targetWord, locale); + final String word = HandlingType.shouldBeLowerCased(wordHandlingType) ? + targetWord.toLowerCase(locale) : targetWord; + if (distracterFilter.isDistracterToWordsInDictionaries(ngramContext, targetWord, locale)) { + // The word is a distracter. + return null; + } + return new WordInputEventForPersonalization(word, ngramContext, + !HandlingType.shouldBeHandledAsOov(wordHandlingType), timestamp); + } +} diff --git a/native/dicttoolkit/Android.mk b/native/dicttoolkit/Android.mk new file mode 100644 index 000000000..118682dfc --- /dev/null +++ b/native/dicttoolkit/Android.mk @@ -0,0 +1,67 @@ +# 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. + +ifeq (,$(TARGET_BUILD_APPS)) + +# Only build if it's explicitly requested, or running mm/mmm. +ifneq ($(ONE_SHOT_MAKEFILE)$(filter $(MAKECMDGOALS),dicttoolkit),) + +# HACK: Temporarily disable host tool build on Mac until the build system is ready for C++11. +LATINIME_HOST_OSNAME := $(shell uname -s) +ifneq ($(LATINIME_HOST_OSNAME), Darwin) # TODO: Remove this + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LATIN_IME_CORE_PATH := $(LOCAL_PATH)/../jni + +LATIN_IME_DICT_TOOLKIT_SRC_DIR := src +LATIN_IME_CORE_SRC_DIR := ../jni/src + +LOCAL_CFLAGS += -Werror -Wall -Wextra -Weffc++ -Wformat=2 -Wcast-qual -Wcast-align \ + -Wwrite-strings -Wfloat-equal -Wpointer-arith -Winit-self -Wredundant-decls \ + -Woverloaded-virtual -Wsign-promo -Wno-system-headers + +# To suppress compiler warnings for unused variables/functions used for debug features etc. +LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function +LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function + +include $(LOCAL_PATH)/NativeFileList.mk +include $(LATIN_IME_CORE_PATH)/NativeFileList.mk + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_DICT_TOOLKIT_SRC_DIR) \ + $(LATIN_IME_CORE_PATH)/$(LATIN_IME_CORE_SRC_DIR) + +LOCAL_SRC_FILES := $(LATIN_IME_DICT_TOOLKIT_MAIN_SRC_FILES) \ + $(addprefix $(LATIN_IME_DICT_TOOLKIT_SRC_DIR)/, $(LATIN_IME_DICT_TOOLKIT_SRC_FILES)) \ + $(addprefix $(LATIN_IME_CORE_SRC_DIR)/, $(LATIN_IME_CORE_SRC_FILES)) + +LOCAL_MODULE := dicttoolkit +LOCAL_MODULE_TAGS := optional + +LOCAL_CLANG := true +LOCAL_CXX_STL := libc++ + +include $(BUILD_HOST_EXECUTABLE) +#################### Clean up the tmp vars +include $(LOCAL_PATH)/CleanupNativeFileList.mk +#################### Unit test +include $(LOCAL_PATH)/UnitTests.mk + +endif # Darwin - TODO: Remove this + +endif + +endif # TARGET_BUILD_APPS diff --git a/native/dicttoolkit/CleanupNativeFileList.mk b/native/dicttoolkit/CleanupNativeFileList.mk new file mode 100644 index 000000000..b804b41ed --- /dev/null +++ b/native/dicttoolkit/CleanupNativeFileList.mk @@ -0,0 +1,17 @@ +# 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. + +LATIN_IME_DICT_TOOLKIT_MAIN_SRC_FILES := +LATIN_IME_DICT_TOOLKIT_SRC_FILES := +LATIN_IME_DICT_TOOLKIT_TEST_FILES := diff --git a/native/dicttoolkit/NativeFileList.mk b/native/dicttoolkit/NativeFileList.mk new file mode 100644 index 000000000..b6be9c541 --- /dev/null +++ b/native/dicttoolkit/NativeFileList.mk @@ -0,0 +1,21 @@ +# 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. + +LATIN_IME_DICT_TOOLKIT_MAIN_SRC_FILES := \ + dict_toolkit_main.cpp + +LATIN_IME_DICT_TOOLKIT_SRC_FILES := + +LATIN_IME_DICT_TOOLKIT_TEST_FILES := \ + dict_toolkit_defines_test.cpp diff --git a/native/dicttoolkit/UnitTests.mk b/native/dicttoolkit/UnitTests.mk new file mode 100644 index 000000000..d568db44a --- /dev/null +++ b/native/dicttoolkit/UnitTests.mk @@ -0,0 +1,68 @@ +# 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. + +ifeq (,$(TARGET_BUILD_APPS)) + +LOCAL_PATH := $(call my-dir) + +###################################### +include $(CLEAR_VARS) + +LATIN_IME_CORE_PATH := $(LOCAL_PATH)/../jni + +LATIN_IME_DICT_TOOLKIT_SRC_DIR := src +LATIN_IME_CORE_SRC_DIR := ../jni/src +LATIN_DICT_TOOLKIT_TEST_SRC_DIR := tests + +include $(LOCAL_PATH)/NativeFileList.mk +include $(LATIN_IME_CORE_PATH)/NativeFileList.mk + +# TODO: Remove -std=c++11 once it is set by default on host build. +LATIN_IME_SRC_DIR := src +LOCAL_ADDRESS_SANITIZER := true +LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function +LOCAL_CLANG := true +LOCAL_CXX_STL := libc++ +LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_DICT_TOOLKIT_SRC_DIR) \ + $(LATIN_IME_CORE_PATH)/$(LATIN_IME_CORE_SRC_DIR) +LOCAL_MODULE := liblatinime_dicttoolkit_host_static_for_unittests +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := \ + $(addprefix $(LATIN_IME_DICT_TOOLKIT_SRC_DIR)/, $(LATIN_IME_DICT_TOOLKIT_SRC_FILES)) \ + $(addprefix $(LATIN_IME_CORE_SRC_DIR)/, $(LATIN_IME_CORE_SRC_FILES)) +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +# TODO: Remove -std=c++11 once it is set by default on host build. +LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function +LOCAL_CLANG := true +LOCAL_CXX_STL := libc++ +LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_DICT_TOOLKIT_SRC_DIR) \ + $(LATIN_IME_CORE_PATH)/$(LATIN_IME_CORE_SRC_DIR) +LOCAL_MODULE := dicttoolkit_unittests +LOCAL_MODULE_TAGS := tests +LOCAL_SRC_FILES := \ + $(addprefix $(LATIN_DICT_TOOLKIT_TEST_SRC_DIR)/, $(LATIN_IME_DICT_TOOLKIT_TEST_FILES)) +LOCAL_STATIC_LIBRARIES += liblatinime_dicttoolkit_host_static_for_unittests +include $(BUILD_HOST_NATIVE_TEST) + +include $(LOCAL_PATH)/CleanupNativeFileList.mk + +#################### Clean up the tmp vars +LATINIME_HOST_OSNAME := +LATIN_IME_SRC_DIR := +LATIN_IME_TEST_SRC_DIR := + +endif # TARGET_BUILD_APPS diff --git a/native/dicttoolkit/dict_toolkit_main.cpp b/native/dicttoolkit/dict_toolkit_main.cpp new file mode 100644 index 000000000..d71b50eb4 --- /dev/null +++ b/native/dicttoolkit/dict_toolkit_main.cpp @@ -0,0 +1,23 @@ +/* + * 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. + */ + +#include <cstdio> + +int main(int argc, char **argv) { + // TODO: Implement. + printf("%s\n", argv[0]); + return 0; +} diff --git a/native/dicttoolkit/run_tests.sh b/native/dicttoolkit/run_tests.sh new file mode 100755 index 000000000..44c99c144 --- /dev/null +++ b/native/dicttoolkit/run_tests.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright 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. + +# check script arguments +if [[ $(type -t mmm) != function ]]; then +if [[ ${BASH_SOURCE[0]} != $0 ]]; then return; else exit 1; fi +fi + +# Host build is never supported in unbundled (NDK/tapas) build +if [[ -n $TARGET_BUILD_APPS ]]; then + echo "Host build is never supported in tapas build." 1>&2 + echo "Use lunch command instead." 1>&2 + if [[ ${BASH_SOURCE[0]} != $0 ]]; then return; else exit 1; fi +fi + +test_name=dicttoolkit_unittests + +pushd $PWD > /dev/null +cd $(gettop) +(mmm -j16 packages/inputmethods/LatinIME/native/dicttoolkit) || (make -j16 $test_name) +$ANDROID_HOST_OUT/bin/$test_name +popd > /dev/null diff --git a/native/dicttoolkit/src/dict_toolkit_defines.h b/native/dicttoolkit/src/dict_toolkit_defines.h new file mode 100644 index 000000000..2a2104e26 --- /dev/null +++ b/native/dicttoolkit/src/dict_toolkit_defines.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef LATINIME_DICT_TOOLKIT_DEFINES_H +#define LATINIME_DICT_TOOLKIT_DEFINES_H + +#include "defines.h" + +#endif // LATINIME_DICT_TOOLKIT_DEFINES_H diff --git a/native/dicttoolkit/tests/dict_toolkit_defines_test.cpp b/native/dicttoolkit/tests/dict_toolkit_defines_test.cpp new file mode 100644 index 000000000..3445bd0c5 --- /dev/null +++ b/native/dicttoolkit/tests/dict_toolkit_defines_test.cpp @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#include "dict_toolkit_defines.h" + +#include <gtest/gtest.h> + +namespace latinime { +namespace dicttoolkit { +namespace { + +// Initial trivial test case. +TEST(DictToolkitDefinesTest, TestKeycodeSpace) { + EXPECT_EQ(' ', KEYCODE_SPACE); +} + +} // namespace +} // namespace dicttoolkit +} // namespace latinime diff --git a/native/jni/HostUnitTests.mk b/native/jni/HostUnitTests.mk index 6967d9b87..e30d50a2e 100644 --- a/native/jni/HostUnitTests.mk +++ b/native/jni/HostUnitTests.mk @@ -12,6 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Host build is never supported in unbundled (NDK/tapas) build +ifeq (,$(TARGET_BUILD_APPS)) + # HACK: Temporarily disable host tool build on Mac until the build system is ready for C++11. LATINIME_HOST_OSNAME := $(shell uname -s) ifneq ($(LATINIME_HOST_OSNAME), Darwin) # TODO: Remove this @@ -26,8 +29,10 @@ include $(LOCAL_PATH)/NativeFileList.mk #################### Host library for unit test # TODO: Remove -std=c++11 once it is set by default on host build. LATIN_IME_SRC_DIR := src +LOCAL_ADDRESS_SANITIZER := true LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function LOCAL_CLANG := true +LOCAL_CXX_STL := libc++ LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_SRC_DIR) LOCAL_MODULE := liblatinime_host_static_for_unittests LOCAL_MODULE_TAGS := optional @@ -37,9 +42,11 @@ include $(BUILD_HOST_STATIC_LIBRARY) #################### Host native tests include $(CLEAR_VARS) LATIN_IME_TEST_SRC_DIR := tests +LOCAL_ADDRESS_SANITIZER := true # TODO: Remove -std=c++11 once it is set by default on host build. LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function LOCAL_CLANG := true +LOCAL_CXX_STL := libc++ LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_SRC_DIR) LOCAL_MODULE := liblatinime_host_unittests LOCAL_MODULE_TAGS := tests @@ -47,10 +54,13 @@ LOCAL_SRC_FILES := $(addprefix $(LATIN_IME_TEST_SRC_DIR)/, $(LATIN_IME_CORE_TEST LOCAL_STATIC_LIBRARIES += liblatinime_host_static_for_unittests include $(BUILD_HOST_NATIVE_TEST) +include $(LOCAL_PATH)/CleanupNativeFileList.mk + endif # Darwin - TODO: Remove this +endif # TARGET_BUILD_APPS + #################### Clean up the tmp vars LATINIME_HOST_OSNAME := LATIN_IME_SRC_DIR := LATIN_IME_TEST_SRC_DIR := -include $(LOCAL_PATH)/CleanupNativeFileList.mk diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk index b896f386f..55bb68344 100644 --- a/native/jni/NativeFileList.mk +++ b/native/jni/NativeFileList.mk @@ -40,6 +40,7 @@ LATIN_IME_CORE_SRC_FILES := \ proximity_info_state_utils.cpp) \ suggest/core/policy/weighting.cpp \ suggest/core/session/dic_traverse_session.cpp \ + suggest/core/session/ngram_context.cpp \ $(addprefix suggest/core/result/, \ suggestion_results.cpp \ suggestions_output_utils.cpp) \ @@ -55,7 +56,7 @@ LATIN_IME_CORE_SRC_FILES := \ dynamic_pt_updating_helper.cpp \ dynamic_pt_writing_utils.cpp \ patricia_trie_reading_utils.cpp \ - shortcut/shortcut_list_reading_utils.cpp ) \ + shortcut/shortcut_list_reading_utils.cpp) \ $(addprefix suggest/policyimpl/dictionary/structure/v2/, \ patricia_trie_policy.cpp \ ver2_patricia_trie_node_reader.cpp \ @@ -70,7 +71,9 @@ LATIN_IME_CORE_SRC_FILES := \ ver4_patricia_trie_writing_helper.cpp \ ver4_pt_node_array_reader.cpp) \ $(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \ + dynamic_language_model_probability_utils.cpp \ language_model_dict_content.cpp \ + language_model_dict_content_global_counters.cpp \ shortcut_dict_content.cpp \ sparse_table_dict_content.cpp \ terminal_position_lookup_table.cpp) \ @@ -82,6 +85,7 @@ LATIN_IME_CORE_SRC_FILES := \ forgetting_curve_utils.cpp \ format_utils.cpp \ mmapped_buffer.cpp \ + probability_utils.cpp \ sparse_table.cpp \ trie_map.cpp ) \ suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \ @@ -127,11 +131,13 @@ LATIN_IME_CORE_TEST_FILES := \ suggest/core/layout/normal_distribution_2d_test.cpp \ suggest/policyimpl/dictionary/header/header_read_write_utils_test.cpp \ suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp \ + suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp \ suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp \ suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table_test.cpp \ suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer_test.cpp \ suggest/policyimpl/dictionary/utils/byte_array_utils_test.cpp \ suggest/policyimpl/dictionary/utils/format_utils_test.cpp \ + suggest/policyimpl/dictionary/utils/probability_utils_test.cpp \ suggest/policyimpl/dictionary/utils/sparse_table_test.cpp \ suggest/policyimpl/dictionary/utils/trie_map_test.cpp \ suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy_test.cpp \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index f8dadb488..118f600bb 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -327,8 +327,8 @@ static jint latinime_BinaryDictionary_getNextWord(JNIEnv *env, jclass clazz, static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz, jlong dict, jintArray word, jboolean isBeginningOfSentence, jintArray outCodePoints, - jbooleanArray outFlags, jintArray outProbabilityInfo, jobject /* outNgramPrevWordsArray */, - jobject /* outNgramPrevWordIsBeginningOfSentenceArray */, jobject outNgramTargets, + jbooleanArray outFlags, jintArray outProbabilityInfo, jobject outNgramPrevWordsArray, + jobject outNgramPrevWordIsBeginningOfSentenceArray, jobject outNgramTargets, jobject outNgramProbabilityInfo, jobject outShortcutTargets, jobject outShortcutProbabilities) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); @@ -352,13 +352,14 @@ static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz, const WordProperty wordProperty = dictionary->getWordProperty( CodePointArrayView(wordCodePoints, codePointCount)); wordProperty.outputProperties(env, outCodePoints, outFlags, outProbabilityInfo, + outNgramPrevWordsArray, outNgramPrevWordIsBeginningOfSentenceArray, outNgramTargets, outNgramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); } static bool latinime_BinaryDictionary_addUnigramEntry(JNIEnv *env, jclass clazz, jlong dict, jintArray word, jint probability, jintArray shortcutTarget, jint shortcutProbability, - jboolean isBeginningOfSentence, jboolean isNotAWord, jboolean isBlacklisted, + jboolean isBeginningOfSentence, jboolean isNotAWord, jboolean isPossiblyOffensive, jint timestamp) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { @@ -377,8 +378,8 @@ static bool latinime_BinaryDictionary_addUnigramEntry(JNIEnv *env, jclass clazz, } // Use 1 for count to indicate the word has inputted. const UnigramProperty unigramProperty(isBeginningOfSentence, isNotAWord, - isBlacklisted, probability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */), - std::move(shortcuts)); + isPossiblyOffensive, probability, HistoricalInfo(timestamp, 0 /* level */, + 1 /* count */), std::move(shortcuts)); return dictionary->addUnigramEntry(CodePointArrayView(codePoints, codePointCount), &unigramProperty); } @@ -409,9 +410,10 @@ static bool latinime_BinaryDictionary_addNgramEntry(JNIEnv *env, jclass clazz, j int wordCodePoints[wordLength]; env->GetIntArrayRegion(word, 0, wordLength, wordCodePoints); // Use 1 for count to indicate the ngram has inputted. - const NgramProperty ngramProperty(CodePointArrayView(wordCodePoints, wordLength).toVector(), + const NgramProperty ngramProperty(ngramContext, + CodePointArrayView(wordCodePoints, wordLength).toVector(), probability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */)); - return dictionary->addNgramEntry(&ngramContext, &ngramProperty); + return dictionary->addNgramEntry(&ngramProperty); } static bool latinime_BinaryDictionary_removeNgramEntry(JNIEnv *env, jclass clazz, jlong dict, @@ -451,97 +453,60 @@ static bool latinime_BinaryDictionary_updateEntriesForWordWithNgramContext(JNIEn historicalInfo); } -// Returns how many language model params are processed. -static int latinime_BinaryDictionary_addMultipleDictionaryEntries(JNIEnv *env, jclass clazz, - jlong dict, jobjectArray languageModelParams, jint startIndex) { +// Returns how many input events are processed. +static int latinime_BinaryDictionary_updateEntriesForInputEvents(JNIEnv *env, jclass clazz, + jlong dict, jobjectArray inputEvents, jint startIndex) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { return 0; } - jsize languageModelParamCount = env->GetArrayLength(languageModelParams); - if (languageModelParamCount == 0 || startIndex >= languageModelParamCount) { + jsize inputEventCount = env->GetArrayLength(inputEvents); + if (inputEventCount == 0 || startIndex >= inputEventCount) { return 0; } - jobject languageModelParam = env->GetObjectArrayElement(languageModelParams, 0); - jclass languageModelParamClass = env->GetObjectClass(languageModelParam); - env->DeleteLocalRef(languageModelParam); - - jfieldID word0FieldId = env->GetFieldID(languageModelParamClass, "mWord0", "[I"); - jfieldID word1FieldId = env->GetFieldID(languageModelParamClass, "mWord1", "[I"); - jfieldID unigramProbabilityFieldId = - env->GetFieldID(languageModelParamClass, "mUnigramProbability", "I"); - jfieldID bigramProbabilityFieldId = - env->GetFieldID(languageModelParamClass, "mBigramProbability", "I"); - jfieldID timestampFieldId = - env->GetFieldID(languageModelParamClass, "mTimestamp", "I"); - jfieldID shortcutTargetFieldId = - env->GetFieldID(languageModelParamClass, "mShortcutTarget", "[I"); - jfieldID shortcutProbabilityFieldId = - env->GetFieldID(languageModelParamClass, "mShortcutProbability", "I"); - jfieldID isNotAWordFieldId = - env->GetFieldID(languageModelParamClass, "mIsNotAWord", "Z"); - jfieldID isBlacklistedFieldId = - env->GetFieldID(languageModelParamClass, "mIsBlacklisted", "Z"); - env->DeleteLocalRef(languageModelParamClass); - - for (int i = startIndex; i < languageModelParamCount; ++i) { - jobject languageModelParam = env->GetObjectArrayElement(languageModelParams, i); - // languageModelParam is a set of params for word1; thus, word1 cannot be null. On the - // other hand, word0 can be null and then it means the set of params doesn't contain bigram - // information. - jintArray word0 = static_cast<jintArray>( - env->GetObjectField(languageModelParam, word0FieldId)); - jsize word0Length = word0 ? env->GetArrayLength(word0) : 0; - int word0CodePoints[word0Length]; - if (word0) { - env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); - } - jintArray word1 = static_cast<jintArray>( - env->GetObjectField(languageModelParam, word1FieldId)); - jsize word1Length = env->GetArrayLength(word1); - int word1CodePoints[word1Length]; - env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); - jint unigramProbability = env->GetIntField(languageModelParam, unigramProbabilityFieldId); - jint timestamp = env->GetIntField(languageModelParam, timestampFieldId); - jboolean isNotAWord = env->GetBooleanField(languageModelParam, isNotAWordFieldId); - jboolean isBlacklisted = env->GetBooleanField(languageModelParam, isBlacklistedFieldId); - jintArray shortcutTarget = static_cast<jintArray>( - env->GetObjectField(languageModelParam, shortcutTargetFieldId)); - std::vector<UnigramProperty::ShortcutProperty> shortcuts; - { - std::vector<int> shortcutTargetCodePoints; - JniDataUtils::jintarrayToVector(env, shortcutTarget, &shortcutTargetCodePoints); - if (!shortcutTargetCodePoints.empty()) { - jint shortcutProbability = - env->GetIntField(languageModelParam, shortcutProbabilityFieldId); - shortcuts.emplace_back(std::move(shortcutTargetCodePoints), shortcutProbability); - } - } + jobject inputEvent = env->GetObjectArrayElement(inputEvents, 0); + jclass wordInputEventClass = env->GetObjectClass(inputEvent); + env->DeleteLocalRef(inputEvent); + + jfieldID targetWordFieldId = env->GetFieldID(wordInputEventClass, "mTargetWord", "[I"); + jfieldID prevWordCountFieldId = env->GetFieldID(wordInputEventClass, "mPrevWordsCount", "I"); + jfieldID prevWordArrayFieldId = env->GetFieldID(wordInputEventClass, "mPrevWordArray", "[[I"); + jfieldID isPrevWordBoSArrayFieldId = + env->GetFieldID(wordInputEventClass, "mIsPrevWordBeginningOfSentenceArray", "[Z"); + jfieldID isValidFieldId = env->GetFieldID(wordInputEventClass, "mIsValid", "Z"); + jfieldID timestampFieldId = env->GetFieldID(wordInputEventClass, "mTimestamp", "I"); + env->DeleteLocalRef(wordInputEventClass); + + for (int i = startIndex; i < inputEventCount; ++i) { + jobject inputEvent = env->GetObjectArrayElement(inputEvents, i); + jintArray targetWord = static_cast<jintArray>( + env->GetObjectField(inputEvent, targetWordFieldId)); + jsize wordLength = env->GetArrayLength(targetWord); + int wordCodePoints[wordLength]; + env->GetIntArrayRegion(targetWord, 0, wordLength, wordCodePoints); + env->DeleteLocalRef(targetWord); + + jint prevWordCount = env->GetIntField(inputEvent, prevWordCountFieldId); + jobjectArray prevWordArray = + static_cast<jobjectArray>(env->GetObjectField(inputEvent, prevWordArrayFieldId)); + jbooleanArray isPrevWordBeginningOfSentenceArray = static_cast<jbooleanArray>( + env->GetObjectField(inputEvent, isPrevWordBoSArrayFieldId)); + jboolean isValid = env->GetBooleanField(inputEvent, isValidFieldId); + jint timestamp = env->GetIntField(inputEvent, timestampFieldId); + const NgramContext ngramContext = JniDataUtils::constructNgramContext(env, + prevWordArray, isPrevWordBeginningOfSentenceArray, prevWordCount); // Use 1 for count to indicate the word has inputted. - const UnigramProperty unigramProperty(false /* isBeginningOfSentence */, isNotAWord, - isBlacklisted, unigramProbability, - HistoricalInfo(timestamp, 0 /* level */, 1 /* count */), std::move(shortcuts)); - dictionary->addUnigramEntry(CodePointArrayView(word1CodePoints, word1Length), - &unigramProperty); - if (word0) { - jint bigramProbability = env->GetIntField(languageModelParam, bigramProbabilityFieldId); - // Use 1 for count to indicate the bigram has inputted. - const NgramProperty ngramProperty( - CodePointArrayView(word1CodePoints, word1Length).toVector(), - bigramProbability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */)); - const NgramContext ngramContext(word0CodePoints, word0Length, - false /* isBeginningOfSentence */); - dictionary->addNgramEntry(&ngramContext, &ngramProperty); - } + dictionary->updateEntriesForWordWithNgramContext(&ngramContext, + CodePointArrayView(wordCodePoints, wordLength), isValid, + HistoricalInfo(timestamp, 0 /* level */, 1 /* count */)); if (dictionary->needsToRunGC(true /* mindsBlockByGC */)) { return i + 1; } - env->DeleteLocalRef(word0); - env->DeleteLocalRef(word1); - env->DeleteLocalRef(shortcutTarget); - env->DeleteLocalRef(languageModelParam); + env->DeleteLocalRef(prevWordArray); + env->DeleteLocalRef(isPrevWordBeginningOfSentenceArray); + env->DeleteLocalRef(inputEvent); } - return languageModelParamCount; + return inputEventCount; } static jstring latinime_BinaryDictionary_getProperty(JNIEnv *env, jclass clazz, jlong dict, @@ -627,8 +592,7 @@ static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, j } } while (token != 0); - // Add bigrams. - // TODO: Support ngrams. + // Add ngrams. do { token = dictionary->getNextWordAndNextToken(token, wordCodePoints, &wordCodePointCount); const WordProperty wordProperty = dictionary->getWordProperty( @@ -641,11 +605,8 @@ static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, j return false; } } - const NgramContext ngramContext(wordCodePoints, wordCodePointCount, - wordProperty.getUnigramProperty()->representsBeginningOfSentence()); for (const NgramProperty &ngramProperty : *wordProperty.getNgramProperties()) { - if (!dictionaryStructureWithBufferPolicy->addNgramEntry(&ngramContext, - &ngramProperty)) { + if (!dictionaryStructureWithBufferPolicy->addNgramEntry(&ngramProperty)) { LogUtils::logToJava(env, "Cannot add ngram to the new dict."); return false; } @@ -755,10 +716,10 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_updateEntriesForWordWithNgramContext) }, { - const_cast<char *>("addMultipleDictionaryEntriesNative"), + const_cast<char *>("updateEntriesForInputEventsNative"), const_cast<char *>( - "(J[Lcom/android/inputmethod/latin/utils/LanguageModelParam;I)I"), - reinterpret_cast<void *>(latinime_BinaryDictionary_addMultipleDictionaryEntries) + "(J[Lcom/android/inputmethod/latin/utils/WordInputEventForPersonalization;I)I"), + reinterpret_cast<void *>(latinime_BinaryDictionary_updateEntriesForInputEvents) }, { const_cast<char *>("getPropertyNative"), diff --git a/native/jni/run-tests.sh b/native/jni/run-tests.sh index 3da45270d..a7fa82d9b 100755 --- a/native/jni/run-tests.sh +++ b/native/jni/run-tests.sh @@ -48,6 +48,13 @@ if [[ $show_usage == yes ]]; then if [[ ${BASH_SOURCE[0]} != $0 ]]; then return; else exit 1; fi fi +# Host build is never supported in unbundled (NDK/tapas) build +if [[ $enable_host_test == yes && -n $TARGET_BUILD_APPS ]]; then + echo "Host build is never supported in tapas build." 1>&2 + echo "Use lunch command instead." 1>&2 + if [[ ${BASH_SOURCE[0]} != $0 ]]; then return; else exit 1; fi +fi + target_test_name=liblatinime_target_unittests host_test_name=liblatinime_host_unittests diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index 7d2898b7a..ea438922f 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -74,8 +74,9 @@ namespace latinime { } const WordAttributes wordAttributes = dictionaryStructurePolicy->getWordAttributesInContext( dicNode->getPrevWordIds(), dicNode->getWordId(), multiBigramMap); - if (dicNode->hasMultipleWords() - && (wordAttributes.isBlacklisted() || wordAttributes.isNotAWord())) { + if (wordAttributes.getProbability() == NOT_A_PROBABILITY + || (dicNode->hasMultipleWords() + && (wordAttributes.isBlacklisted() || wordAttributes.isNotAWord()))) { return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); } // TODO: This equation to calculate the improbability looks unreasonable. Investigate this. diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index 697e99ffb..6a5df9d95 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -81,6 +81,9 @@ void Dictionary::NgramListenerForPrediction::onVisitEntry(const int ngramProbabi } const WordAttributes wordAttributes = mDictStructurePolicy->getWordAttributesInContext( mPrevWordIds, targetWordId, nullptr /* multiBigramMap */); + if (wordAttributes.getProbability() == NOT_A_PROBABILITY) { + return; + } mSuggestionResults->addPrediction(targetWordCodePoints, codePointCount, wordAttributes.getProbability()); } @@ -140,10 +143,9 @@ bool Dictionary::removeUnigramEntry(const CodePointArrayView codePoints) { return mDictionaryStructureWithBufferPolicy->removeUnigramEntry(codePoints); } -bool Dictionary::addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty) { +bool Dictionary::addNgramEntry(const NgramProperty *const ngramProperty) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->addNgramEntry(ngramContext, ngramProperty); + return mDictionaryStructureWithBufferPolicy->addNgramEntry(ngramProperty); } bool Dictionary::removeNgramEntry(const NgramContext *const ngramContext, diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index 843aec473..a5e986d15 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -85,8 +85,7 @@ class Dictionary { bool removeUnigramEntry(const CodePointArrayView codePoints); - bool addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty); + bool addNgramEntry(const NgramProperty *const ngramProperty); bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView codePoints); diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp index 1e2494e92..8f07ce275 100644 --- a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp @@ -31,6 +31,7 @@ const ErrorTypeUtils::ErrorType ErrorTypeUtils::NEW_WORD = 0x100; const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH = NOT_AN_ERROR | MATCH_WITH_WRONG_CASE | MATCH_WITH_MISSING_ACCENT | MATCH_WITH_DIGRAPH; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_A_PERFECT_MATCH = NOT_AN_ERROR; const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION = diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.h b/native/jni/src/suggest/core/dictionary/error_type_utils.h index fd1d5fcff..e92c509fa 100644 --- a/native/jni/src/suggest/core/dictionary/error_type_utils.h +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.h @@ -52,6 +52,10 @@ class ErrorTypeUtils { return (containedErrorTypes & ~ERRORS_TREATED_AS_AN_EXACT_MATCH) == 0; } + static bool isPerfectMatch(const ErrorType containedErrorTypes) { + return (containedErrorTypes & ~ERRORS_TREATED_AS_A_PERFECT_MATCH) == 0; + } + static bool isExactMatchWithIntentionalOmission(const ErrorType containedErrorTypes) { return (containedErrorTypes & ~ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION) == 0; @@ -73,6 +77,7 @@ class ErrorTypeUtils { DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorTypeUtils); static const ErrorType ERRORS_TREATED_AS_AN_EXACT_MATCH; + static const ErrorType ERRORS_TREATED_AS_A_PERFECT_MATCH; static const ErrorType ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION; }; } // namespace latinime diff --git a/native/jni/src/suggest/core/dictionary/ngram_listener.h b/native/jni/src/suggest/core/dictionary/ngram_listener.h index e9b3c1aaf..2eb5e9fd1 100644 --- a/native/jni/src/suggest/core/dictionary/ngram_listener.h +++ b/native/jni/src/suggest/core/dictionary/ngram_listener.h @@ -26,6 +26,8 @@ namespace latinime { */ class NgramListener { public: + // ngramProbability is always 0 for v403 decaying dictionary. + // TODO: Remove ngramProbability. virtual void onVisitEntry(const int ngramProbability, const int targetWordId) = 0; virtual ~NgramListener() {}; diff --git a/native/jni/src/suggest/core/dictionary/property/historical_info.h b/native/jni/src/suggest/core/dictionary/property/historical_info.h index f9bd6fd8c..e5ce1ea25 100644 --- a/native/jni/src/suggest/core/dictionary/property/historical_info.h +++ b/native/jni/src/suggest/core/dictionary/property/historical_info.h @@ -38,6 +38,7 @@ class HistoricalInfo { return mTimestamp; } + // TODO: Remove int getLevel() const { return mLevel; } diff --git a/native/jni/src/suggest/core/dictionary/property/ngram_property.h b/native/jni/src/suggest/core/dictionary/property/ngram_property.h index 8709799f9..e67b4da31 100644 --- a/native/jni/src/suggest/core/dictionary/property/ngram_property.h +++ b/native/jni/src/suggest/core/dictionary/property/ngram_property.h @@ -21,15 +21,20 @@ #include "defines.h" #include "suggest/core/dictionary/property/historical_info.h" +#include "suggest/core/session/ngram_context.h" namespace latinime { class NgramProperty { public: - NgramProperty(const std::vector<int> &&targetCodePoints, const int probability, - const HistoricalInfo historicalInfo) - : mTargetCodePoints(std::move(targetCodePoints)), mProbability(probability), - mHistoricalInfo(historicalInfo) {} + NgramProperty(const NgramContext &ngramContext, const std::vector<int> &&targetCodePoints, + const int probability, const HistoricalInfo historicalInfo) + : mNgramContext(ngramContext), mTargetCodePoints(std::move(targetCodePoints)), + mProbability(probability), mHistoricalInfo(historicalInfo) {} + + const NgramContext *getNgramContext() const { + return &mNgramContext; + } const std::vector<int> *getTargetCodePoints() const { return &mTargetCodePoints; @@ -48,6 +53,7 @@ class NgramProperty { DISALLOW_DEFAULT_CONSTRUCTOR(NgramProperty); DISALLOW_ASSIGNMENT_OPERATOR(NgramProperty); + const NgramContext mNgramContext; const std::vector<int> mTargetCodePoints; const int mProbability; const HistoricalInfo mHistoricalInfo; diff --git a/native/jni/src/suggest/core/dictionary/property/unigram_property.h b/native/jni/src/suggest/core/dictionary/property/unigram_property.h index 5ed2e2602..f194f979a 100644 --- a/native/jni/src/suggest/core/dictionary/property/unigram_property.h +++ b/native/jni/src/suggest/core/dictionary/property/unigram_property.h @@ -49,21 +49,44 @@ class UnigramProperty { }; UnigramProperty() - : mRepresentsBeginningOfSentence(false), mIsNotAWord(false), mIsBlacklisted(false), - mProbability(NOT_A_PROBABILITY), mHistoricalInfo(), mShortcuts() {} + : mRepresentsBeginningOfSentence(false), mIsNotAWord(false), + mIsBlacklisted(false), mIsPossiblyOffensive(false), mProbability(NOT_A_PROBABILITY), + mHistoricalInfo(), mShortcuts() {} + // In contexts which do not support the Blacklisted flag (v2, v4<403) UnigramProperty(const bool representsBeginningOfSentence, const bool isNotAWord, - const bool isBlacklisted, const int probability, const HistoricalInfo historicalInfo, - const std::vector<ShortcutProperty> &&shortcuts) + const bool isPossiblyOffensive, const int probability, + const HistoricalInfo historicalInfo, const std::vector<ShortcutProperty> &&shortcuts) : mRepresentsBeginningOfSentence(representsBeginningOfSentence), - mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mProbability(probability), + mIsNotAWord(isNotAWord), mIsBlacklisted(false), + mIsPossiblyOffensive(isPossiblyOffensive), mProbability(probability), mHistoricalInfo(historicalInfo), mShortcuts(std::move(shortcuts)) {} - // Without shortcuts. + // Without shortcuts, in contexts which do not support the Blacklisted flag (v2, v4<403) UnigramProperty(const bool representsBeginningOfSentence, const bool isNotAWord, - const bool isBlacklisted, const int probability, const HistoricalInfo historicalInfo) + const bool isPossiblyOffensive, const int probability, + const HistoricalInfo historicalInfo) : mRepresentsBeginningOfSentence(representsBeginningOfSentence), - mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mProbability(probability), + mIsNotAWord(isNotAWord), mIsBlacklisted(false), + mIsPossiblyOffensive(isPossiblyOffensive), mProbability(probability), + mHistoricalInfo(historicalInfo), mShortcuts() {} + + // In contexts which DO support the Blacklisted flag (v403) + UnigramProperty(const bool representsBeginningOfSentence, const bool isNotAWord, + const bool isBlacklisted, const bool isPossiblyOffensive, const int probability, + const HistoricalInfo historicalInfo, const std::vector<ShortcutProperty> &&shortcuts) + : mRepresentsBeginningOfSentence(representsBeginningOfSentence), + mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), + mIsPossiblyOffensive(isPossiblyOffensive), mProbability(probability), + mHistoricalInfo(historicalInfo), mShortcuts(std::move(shortcuts)) {} + + // Without shortcuts, in contexts which DO support the Blacklisted flag (v403) + UnigramProperty(const bool representsBeginningOfSentence, const bool isNotAWord, + const bool isBlacklisted, const bool isPossiblyOffensive, const int probability, + const HistoricalInfo historicalInfo) + : mRepresentsBeginningOfSentence(representsBeginningOfSentence), + mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), + mIsPossiblyOffensive(isPossiblyOffensive), mProbability(probability), mHistoricalInfo(historicalInfo), mShortcuts() {} bool representsBeginningOfSentence() const { @@ -74,13 +97,12 @@ class UnigramProperty { return mIsNotAWord; } - bool isBlacklisted() const { - return mIsBlacklisted; + bool isPossiblyOffensive() const { + return mIsPossiblyOffensive; } - bool isPossiblyOffensive() const { - // TODO: Have dedicated flag. - return mProbability == 0; + bool isBlacklisted() const { + return mIsBlacklisted; } bool hasShortcuts() const { @@ -106,6 +128,7 @@ class UnigramProperty { const bool mRepresentsBeginningOfSentence; const bool mIsNotAWord; const bool mIsBlacklisted; + const bool mIsPossiblyOffensive; const int mProbability; const HistoricalInfo mHistoricalInfo; const std::vector<ShortcutProperty> mShortcuts; diff --git a/native/jni/src/suggest/core/dictionary/property/word_property.cpp b/native/jni/src/suggest/core/dictionary/property/word_property.cpp index caac8fe79..019f0880f 100644 --- a/native/jni/src/suggest/core/dictionary/property/word_property.cpp +++ b/native/jni/src/suggest/core/dictionary/property/word_property.cpp @@ -22,13 +22,14 @@ namespace latinime { void WordProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints, - jbooleanArray outFlags, jintArray outProbabilityInfo, jobject outBigramTargets, - jobject outBigramProbabilities, jobject outShortcutTargets, + jbooleanArray outFlags, jintArray outProbabilityInfo, + jobject outNgramPrevWordsArray, jobject outNgramPrevWordIsBeginningOfSentenceArray, + jobject outNgramTargets, jobject outNgramProbabilities, jobject outShortcutTargets, jobject outShortcutProbabilities) const { JniDataUtils::outputCodePoints(env, outCodePoints, 0 /* start */, MAX_WORD_LENGTH /* maxLength */, mCodePoints.data(), mCodePoints.size(), false /* needsNullTermination */); - jboolean flags[] = {mUnigramProperty.isNotAWord(), mUnigramProperty.isBlacklisted(), + jboolean flags[] = {mUnigramProperty.isNotAWord(), mUnigramProperty.isPossiblyOffensive(), !mNgrams.empty(), mUnigramProperty.hasShortcuts(), mUnigramProperty.representsBeginningOfSentence()}; env->SetBooleanArrayRegion(outFlags, 0 /* start */, NELEMS(flags), flags); @@ -43,16 +44,39 @@ void WordProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints, jclass arrayListClass = env->FindClass("java/util/ArrayList"); jmethodID addMethodId = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); - // Output bigrams. - // TODO: Support n-gram + // Output ngrams. + jclass intArrayClass = env->FindClass("[I"); for (const auto &ngramProperty : mNgrams) { - const std::vector<int> *const word1CodePoints = ngramProperty.getTargetCodePoints(); - jintArray bigramWord1CodePointArray = env->NewIntArray(word1CodePoints->size()); - JniDataUtils::outputCodePoints(env, bigramWord1CodePointArray, 0 /* start */, - word1CodePoints->size(), word1CodePoints->data(), word1CodePoints->size(), - false /* needsNullTermination */); - env->CallBooleanMethod(outBigramTargets, addMethodId, bigramWord1CodePointArray); - env->DeleteLocalRef(bigramWord1CodePointArray); + const NgramContext *const ngramContext = ngramProperty.getNgramContext(); + jobjectArray prevWordWordCodePointsArray = env->NewObjectArray( + ngramContext->getPrevWordCount(), intArrayClass, nullptr); + jbooleanArray prevWordIsBeginningOfSentenceArray = + env->NewBooleanArray(ngramContext->getPrevWordCount()); + for (size_t i = 0; i < ngramContext->getPrevWordCount(); ++i) { + const CodePointArrayView codePoints = ngramContext->getNthPrevWordCodePoints(i + 1); + jintArray prevWordCodePoints = env->NewIntArray(codePoints.size()); + JniDataUtils::outputCodePoints(env, prevWordCodePoints, 0 /* start */, + codePoints.size(), codePoints.data(), codePoints.size(), + false /* needsNullTermination */); + env->SetObjectArrayElement(prevWordWordCodePointsArray, i, prevWordCodePoints); + env->DeleteLocalRef(prevWordCodePoints); + JniDataUtils::putBooleanToArray(env, prevWordIsBeginningOfSentenceArray, i, + ngramContext->isNthPrevWordBeginningOfSentence(i + 1)); + } + env->CallBooleanMethod(outNgramPrevWordsArray, addMethodId, prevWordWordCodePointsArray); + env->CallBooleanMethod(outNgramPrevWordIsBeginningOfSentenceArray, addMethodId, + prevWordIsBeginningOfSentenceArray); + env->DeleteLocalRef(prevWordWordCodePointsArray); + env->DeleteLocalRef(prevWordIsBeginningOfSentenceArray); + + const std::vector<int> *const targetWordCodePoints = ngramProperty.getTargetCodePoints(); + jintArray targetWordCodePointArray = env->NewIntArray(targetWordCodePoints->size()); + JniDataUtils::outputCodePoints(env, targetWordCodePointArray, 0 /* start */, + targetWordCodePoints->size(), targetWordCodePoints->data(), + targetWordCodePoints->size(), false /* needsNullTermination */); + env->CallBooleanMethod(outNgramTargets, addMethodId, targetWordCodePointArray); + env->DeleteLocalRef(targetWordCodePointArray); + const HistoricalInfo &ngramHistoricalInfo = ngramProperty.getHistoricalInfo(); int bigramProbabilityInfo[] = {ngramProperty.getProbability(), ngramHistoricalInfo.getTimestamp(), ngramHistoricalInfo.getLevel(), @@ -60,7 +84,7 @@ void WordProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints, jintArray bigramProbabilityInfoArray = env->NewIntArray(NELEMS(bigramProbabilityInfo)); env->SetIntArrayRegion(bigramProbabilityInfoArray, 0 /* start */, NELEMS(bigramProbabilityInfo), bigramProbabilityInfo); - env->CallBooleanMethod(outBigramProbabilities, addMethodId, bigramProbabilityInfoArray); + env->CallBooleanMethod(outNgramProbabilities, addMethodId, bigramProbabilityInfoArray); env->DeleteLocalRef(bigramProbabilityInfoArray); } diff --git a/native/jni/src/suggest/core/dictionary/property/word_property.h b/native/jni/src/suggest/core/dictionary/property/word_property.h index 0c23e8225..b5314faaa 100644 --- a/native/jni/src/suggest/core/dictionary/property/word_property.h +++ b/native/jni/src/suggest/core/dictionary/property/word_property.h @@ -34,13 +34,15 @@ class WordProperty { : mCodePoints(), mUnigramProperty(), mNgrams() {} WordProperty(const std::vector<int> &&codePoints, const UnigramProperty *const unigramProperty, - const std::vector<NgramProperty> *const bigrams) + const std::vector<NgramProperty> *const ngrams) : mCodePoints(std::move(codePoints)), mUnigramProperty(*unigramProperty), - mNgrams(*bigrams) {} + mNgrams(*ngrams) {} void outputProperties(JNIEnv *const env, jintArray outCodePoints, jbooleanArray outFlags, - jintArray outProbabilityInfo, jobject outBigramTargets, jobject outBigramProbabilities, - jobject outShortcutTargets, jobject outShortcutProbabilities) const; + jintArray outProbabilityInfo, jobject outNgramPrevWordsArray, + jobject outNgramPrevWordIsBeginningOfSentenceArray, jobject outNgramTargets, + jobject outNgramProbabilities, jobject outShortcutTargets, + jobject outShortcutProbabilities) const; const UnigramProperty *getUnigramProperty() const { return &mUnigramProperty; diff --git a/native/jni/src/suggest/core/dictionary/word_attributes.h b/native/jni/src/suggest/core/dictionary/word_attributes.h index 6e9da3570..5351e7d7d 100644 --- a/native/jni/src/suggest/core/dictionary/word_attributes.h +++ b/native/jni/src/suggest/core/dictionary/word_attributes.h @@ -43,6 +43,14 @@ class WordAttributes { return mIsNotAWord; } + // Whether or not a word is possibly offensive. + // * Static dictionaries <v202, as well as dynamic dictionaries <v403, will set this based on + // whether or not the probability of the word is zero. + // * Static dictionaries >=v203 will set this based on the IS_POSSIBLY_OFFENSIVE PtNode flag. + // * Dynamic dictionaries >=v403 will set this based on the IS_POSSIBLY_OFFENSIVE language model + // flag (the PtNode flag IS_BLACKLISTED is ignored and kept as zero) + // + // See the ::getWordAttributes function for each of these dictionary policies for more details. bool isPossiblyOffensive() const { return mIsPossiblyOffensive; } diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h index ceda5c03f..33a0fbc19 100644 --- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h @@ -40,7 +40,6 @@ class UnigramProperty; * This class abstracts the structure of dictionaries. * Implement this policy to support additional dictionaries. */ -// TODO: Use word id instead of terminal PtNode position. class DictionaryStructureWithBufferPolicy { public: typedef std::unique_ptr<DictionaryStructureWithBufferPolicy> StructurePolicyPtr; @@ -81,8 +80,7 @@ class DictionaryStructureWithBufferPolicy { virtual bool removeUnigramEntry(const CodePointArrayView wordCodePoints) = 0; // Returns whether the update was success or not. - virtual bool addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty) = 0; + virtual bool addNgramEntry(const NgramProperty *const ngramProperty) = 0; // Returns whether the update was success or not. virtual bool removeNgramEntry(const NgramContext *const ngramContext, @@ -106,7 +104,6 @@ class DictionaryStructureWithBufferPolicy { virtual void getProperty(const char *const query, const int queryLength, char *const outResult, const int maxResultLength) = 0; - // Used for testing. virtual const WordProperty getWordProperty(const CodePointArrayView wordCodePoints) const = 0; // Method to iterate all words in the dictionary. diff --git a/native/jni/src/suggest/core/policy/scoring.h b/native/jni/src/suggest/core/policy/scoring.h index ce3684a1c..b9dda83ad 100644 --- a/native/jni/src/suggest/core/policy/scoring.h +++ b/native/jni/src/suggest/core/policy/scoring.h @@ -30,7 +30,7 @@ class Scoring { public: virtual int calculateFinalScore(const float compoundDistance, const int inputSize, const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit, - const bool boostExactMatches) const = 0; + const bool boostExactMatches, const bool hasProbabilityZero) const = 0; virtual void getMostProbableString(const DicTraverseSession *const traverseSession, const float weightOfLangModelVsSpatialModel, SuggestionResults *const outSuggestionResults) const = 0; diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp index a06e7d070..450203d98 100644 --- a/native/jni/src/suggest/core/policy/weighting.cpp +++ b/native/jni/src/suggest/core/policy/weighting.cpp @@ -119,7 +119,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return weighting->getSubstitutionCost() + weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_NEW_WORD_SPACE_OMISSION: - return weighting->getNewWordSpatialCost(traverseSession, dicNode, inputStateG); + return weighting->getSpaceOmissionCost(traverseSession, dicNode, inputStateG); case CT_MATCH: return weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_COMPLETION: diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h index bd6b3cf41..863c4eabe 100644 --- a/native/jni/src/suggest/core/policy/weighting.h +++ b/native/jni/src/suggest/core/policy/weighting.h @@ -57,7 +57,7 @@ class Weighting { const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0; - virtual float getNewWordSpatialCost(const DicTraverseSession *const traverseSession, + virtual float getSpaceOmissionCost(const DicTraverseSession *const traverseSession, const DicNode *const dicNode, DicNode_InputStateG *const inputStateG) const = 0; virtual float getNewWordBigramLanguageCost( diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp index 3283f6deb..74db95953 100644 --- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp +++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp @@ -76,6 +76,52 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; weightOfLangModelVsSpatialModelToOutputSuggestions, outSuggestionResults); } +/* static */ bool SuggestionsOutputUtils::shouldBlockWord( + const SuggestOptions *const suggestOptions, const DicNode *const terminalDicNode, + const WordAttributes wordAttributes, const bool isLastWord) { + const bool currentWordExactMatch = + ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes()); + // When we have to block offensive words, non-exact matched offensive words should not be + // output. + const bool shouldBlockOffensiveWords = suggestOptions->blockOffensiveWords(); + + const bool isBlockedOffensiveWord = shouldBlockOffensiveWords && + wordAttributes.isPossiblyOffensive(); + + // This function is called in two situations: + // + // 1) At the end of a search, in which case terminalDicNode will point to the last DicNode + // of the search, and isLastWord will be true. + // "fuck" + // | + // \ terminalDicNode (isLastWord=true, currentWordExactMatch=true) + // In this case, if the current word is an exact match, we will always let the word + // through, even if the user is blocking offensive words (it's exactly what they typed!) + // + // 2) In the middle of the search, when we hit a terminal node, to decide whether or not + // to start a new search at root, to try to match the rest of the input. In this case, + // terminalDicNode will point to the terminal node we just hit, and isLastWord will be + // false. + // "fuckvthis" + // | + // \ terminalDicNode (isLastWord=false, currentWordExactMatch=true) + // + // In this case, we should NOT allow the match through (correcting "fuckthis" to "fuck this" + // when offensive words are blocked would be a bad idea). + // + // In the case of a multi-word correction where the offensive word is typed last (eg. + // for the input "allfuck"), this function will be called with isLastWord==true, but + // currentWordExactMatch==false. So we are OK in this case as well. + // "allfuck" + // | + // \ terminalDicNode (isLastWord=true, currentWordExactMatch=false) + if (isLastWord && currentWordExactMatch) { + return false; + } else { + return isBlockedOffensiveWord; + } +} + /* static */ void SuggestionsOutputUtils::outputSuggestionsOfDicNode( const Scoring *const scoringPolicy, DicTraverseSession *traverseSession, const DicNode *const terminalDicNode, const float weightOfLangModelVsSpatialModel, @@ -98,24 +144,16 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; const bool isExactMatchWithIntentionalOmission = ErrorTypeUtils::isExactMatchWithIntentionalOmission( terminalDicNode->getContainedErrorTypes()); - const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase(); - // Heuristic: We exclude probability=0 first-char-uppercase words from exact match. - // (e.g. "AMD" and "and") - const bool isSafeExactMatch = isExactMatch - && !(wordAttributes.isPossiblyOffensive() && isFirstCharUppercase); const int outputTypeFlags = (wordAttributes.isPossiblyOffensive() ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0) - | ((isSafeExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0) + | ((isExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0) | (isExactMatchWithIntentionalOmission ? Dictionary::KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION : 0); - // Entries that are blacklisted or do not represent a word should not be output. const bool isValidWord = !(wordAttributes.isBlacklisted() || wordAttributes.isNotAWord()); - // When we have to block offensive words, non-exact matched offensive words should not be - // output. - const bool blockOffensiveWords = traverseSession->getSuggestOptions()->blockOffensiveWords(); - const bool isBlockedOffensiveWord = blockOffensiveWords && wordAttributes.isPossiblyOffensive() - && !isSafeExactMatch; + + const bool shouldBlockThisWord = shouldBlockWord(traverseSession->getSuggestOptions(), + terminalDicNode, wordAttributes, true /* isLastWord */); // Increase output score of top typing suggestion to ensure autocorrection. // TODO: Better integration with java side autocorrection logic. @@ -123,11 +161,11 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; compoundDistance, traverseSession->getInputSize(), terminalDicNode->getContainedErrorTypes(), (forceCommitMultiWords && terminalDicNode->hasMultipleWords()), - boostExactMatches); + boostExactMatches, wordAttributes.getProbability() == 0); // Don't output invalid or blocked offensive words. However, we still need to submit their // shortcuts if any. - if (isValidWord && !isBlockedOffensiveWord) { + if (isValidWord && !shouldBlockThisWord) { int codePoints[MAX_WORD_LENGTH]; terminalDicNode->outputResult(codePoints); const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ? diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h index bf8497828..eca1f78b2 100644 --- a/native/jni/src/suggest/core/result/suggestions_output_utils.h +++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h @@ -18,6 +18,7 @@ #define LATINIME_SUGGESTIONS_OUTPUT_UTILS #include "defines.h" +#include "suggest/core/dictionary/word_attributes.h" namespace latinime { @@ -25,11 +26,19 @@ class BinaryDictionaryShortcutIterator; class DicNode; class DicTraverseSession; class Scoring; +class SuggestOptions; class SuggestionResults; class SuggestionsOutputUtils { public: /** + * Returns true if we should block the incoming word, in the context of the user's + * preferences to include or not include possibly offensive words + */ + static bool shouldBlockWord(const SuggestOptions *const suggestOptions, + const DicNode *const terminalDicNode, const WordAttributes wordAttributes, + const bool isLastWord); + /** * Outputs the final list of suggestions (i.e., terminal nodes). */ static void outputSuggestions(const Scoring *const scoringPolicy, diff --git a/native/jni/src/suggest/core/session/ngram_context.cpp b/native/jni/src/suggest/core/session/ngram_context.cpp new file mode 100644 index 000000000..17ef9ae60 --- /dev/null +++ b/native/jni/src/suggest/core/session/ngram_context.cpp @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#include "suggest/core/session/ngram_context.h" + +#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "utils/char_utils.h" + +namespace latinime { + +NgramContext::NgramContext() : mPrevWordCount(0) {} + +NgramContext::NgramContext(const NgramContext &ngramContext) + : mPrevWordCount(ngramContext.mPrevWordCount) { + for (size_t i = 0; i < mPrevWordCount; ++i) { + mPrevWordCodePointCount[i] = ngramContext.mPrevWordCodePointCount[i]; + memmove(mPrevWordCodePoints[i], ngramContext.mPrevWordCodePoints[i], + sizeof(mPrevWordCodePoints[i][0]) * mPrevWordCodePointCount[i]); + mIsBeginningOfSentence[i] = ngramContext.mIsBeginningOfSentence[i]; + } +} + +NgramContext::NgramContext(const int prevWordCodePoints[][MAX_WORD_LENGTH], + const int *const prevWordCodePointCount, const bool *const isBeginningOfSentence, + const size_t prevWordCount) + : mPrevWordCount(std::min(NELEMS(mPrevWordCodePoints), prevWordCount)) { + clear(); + for (size_t i = 0; i < mPrevWordCount; ++i) { + if (prevWordCodePointCount[i] < 0 || prevWordCodePointCount[i] > MAX_WORD_LENGTH) { + continue; + } + memmove(mPrevWordCodePoints[i], prevWordCodePoints[i], + sizeof(mPrevWordCodePoints[i][0]) * prevWordCodePointCount[i]); + mPrevWordCodePointCount[i] = prevWordCodePointCount[i]; + mIsBeginningOfSentence[i] = isBeginningOfSentence[i]; + } +} + +NgramContext::NgramContext(const int *const prevWordCodePoints, const int prevWordCodePointCount, + const bool isBeginningOfSentence) : mPrevWordCount(1) { + clear(); + if (prevWordCodePointCount > MAX_WORD_LENGTH || !prevWordCodePoints) { + return; + } + memmove(mPrevWordCodePoints[0], prevWordCodePoints, + sizeof(mPrevWordCodePoints[0][0]) * prevWordCodePointCount); + mPrevWordCodePointCount[0] = prevWordCodePointCount; + mIsBeginningOfSentence[0] = isBeginningOfSentence; +} + +bool NgramContext::isValid() const { + if (mPrevWordCodePointCount[0] > 0) { + return true; + } + if (mIsBeginningOfSentence[0]) { + return true; + } + return false; +} + +const CodePointArrayView NgramContext::getNthPrevWordCodePoints(const size_t n) const { + if (n <= 0 || n > mPrevWordCount) { + return CodePointArrayView(); + } + return CodePointArrayView(mPrevWordCodePoints[n - 1], mPrevWordCodePointCount[n - 1]); +} + +bool NgramContext::isNthPrevWordBeginningOfSentence(const size_t n) const { + if (n <= 0 || n > mPrevWordCount) { + return false; + } + return mIsBeginningOfSentence[n - 1]; +} + +/* static */ int NgramContext::getWordId( + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, + const int *const wordCodePoints, const int wordCodePointCount, + const bool isBeginningOfSentence, const bool tryLowerCaseSearch) { + if (!dictStructurePolicy || !wordCodePoints || wordCodePointCount > MAX_WORD_LENGTH) { + return NOT_A_WORD_ID; + } + int codePoints[MAX_WORD_LENGTH]; + int codePointCount = wordCodePointCount; + memmove(codePoints, wordCodePoints, sizeof(int) * codePointCount); + if (isBeginningOfSentence) { + codePointCount = CharUtils::attachBeginningOfSentenceMarker(codePoints, codePointCount, + MAX_WORD_LENGTH); + if (codePointCount <= 0) { + return NOT_A_WORD_ID; + } + } + const CodePointArrayView codePointArrayView(codePoints, codePointCount); + const int wordId = dictStructurePolicy->getWordId(codePointArrayView, + false /* forceLowerCaseSearch */); + if (wordId != NOT_A_WORD_ID || !tryLowerCaseSearch) { + // Return the id when when the word was found or doesn't try lower case search. + return wordId; + } + // Check bigrams for lower-cased previous word if original was not found. Useful for + // auto-capitalized words like "The [current_word]". + return dictStructurePolicy->getWordId(codePointArrayView, true /* forceLowerCaseSearch */); +} + +void NgramContext::clear() { + for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { + mPrevWordCodePointCount[i] = 0; + mIsBeginningOfSentence[i] = false; + } +} +} // namespace latinime diff --git a/native/jni/src/suggest/core/session/ngram_context.h b/native/jni/src/suggest/core/session/ngram_context.h index 64c71410f..9b36199c9 100644 --- a/native/jni/src/suggest/core/session/ngram_context.h +++ b/native/jni/src/suggest/core/session/ngram_context.h @@ -20,145 +20,54 @@ #include <array> #include "defines.h" -#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" -#include "utils/char_utils.h" #include "utils/int_array_view.h" namespace latinime { -// Rename to NgramContext. +class DictionaryStructureWithBufferPolicy; + class NgramContext { public: // No prev word information. - NgramContext() : mPrevWordCount(0) { - clear(); - } - - NgramContext(const NgramContext &ngramContext) - : mPrevWordCount(ngramContext.mPrevWordCount) { - for (size_t i = 0; i < mPrevWordCount; ++i) { - mPrevWordCodePointCount[i] = ngramContext.mPrevWordCodePointCount[i]; - memmove(mPrevWordCodePoints[i], ngramContext.mPrevWordCodePoints[i], - sizeof(mPrevWordCodePoints[i][0]) * mPrevWordCodePointCount[i]); - mIsBeginningOfSentence[i] = ngramContext.mIsBeginningOfSentence[i]; - } - } - + NgramContext(); + // Copy constructor to use this class with std::vector and use this class as a return value. + NgramContext(const NgramContext &ngramContext); // Construct from previous words. NgramContext(const int prevWordCodePoints[][MAX_WORD_LENGTH], const int *const prevWordCodePointCount, const bool *const isBeginningOfSentence, - const size_t prevWordCount) - : mPrevWordCount(std::min(NELEMS(mPrevWordCodePoints), prevWordCount)) { - clear(); - for (size_t i = 0; i < mPrevWordCount; ++i) { - if (prevWordCodePointCount[i] < 0 || prevWordCodePointCount[i] > MAX_WORD_LENGTH) { - continue; - } - memmove(mPrevWordCodePoints[i], prevWordCodePoints[i], - sizeof(mPrevWordCodePoints[i][0]) * prevWordCodePointCount[i]); - mPrevWordCodePointCount[i] = prevWordCodePointCount[i]; - mIsBeginningOfSentence[i] = isBeginningOfSentence[i]; - } - } - + const size_t prevWordCount); // Construct from a previous word. NgramContext(const int *const prevWordCodePoints, const int prevWordCodePointCount, - const bool isBeginningOfSentence) : mPrevWordCount(1) { - clear(); - if (prevWordCodePointCount > MAX_WORD_LENGTH || !prevWordCodePoints) { - return; - } - memmove(mPrevWordCodePoints[0], prevWordCodePoints, - sizeof(mPrevWordCodePoints[0][0]) * prevWordCodePointCount); - mPrevWordCodePointCount[0] = prevWordCodePointCount; - mIsBeginningOfSentence[0] = isBeginningOfSentence; - } + const bool isBeginningOfSentence); size_t getPrevWordCount() const { return mPrevWordCount; } - - // TODO: Remove. - const NgramContext getTrimmedNgramContext(const size_t maxPrevWordCount) const { - return NgramContext(mPrevWordCodePoints, mPrevWordCodePointCount, mIsBeginningOfSentence, - std::min(mPrevWordCount, maxPrevWordCount)); - } - - bool isValid() const { - if (mPrevWordCodePointCount[0] > 0) { - return true; - } - if (mIsBeginningOfSentence[0]) { - return true; - } - return false; - } + bool isValid() const; template<size_t N> const WordIdArrayView getPrevWordIds( const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, - std::array<int, N> *const prevWordIdBuffer, const bool tryLowerCaseSearch) const { + WordIdArray<N> *const prevWordIdBuffer, const bool tryLowerCaseSearch) const { for (size_t i = 0; i < std::min(mPrevWordCount, N); ++i) { - prevWordIdBuffer->at(i) = getWordId(dictStructurePolicy, - mPrevWordCodePoints[i], mPrevWordCodePointCount[i], - mIsBeginningOfSentence[i], tryLowerCaseSearch); + prevWordIdBuffer->at(i) = getWordId(dictStructurePolicy, mPrevWordCodePoints[i], + mPrevWordCodePointCount[i], mIsBeginningOfSentence[i], tryLowerCaseSearch); } return WordIdArrayView::fromArray(*prevWordIdBuffer).limit(mPrevWordCount); } // n is 1-indexed. - const CodePointArrayView getNthPrevWordCodePoints(const size_t n) const { - if (n <= 0 || n > mPrevWordCount) { - return CodePointArrayView(); - } - return CodePointArrayView(mPrevWordCodePoints[n - 1], mPrevWordCodePointCount[n - 1]); - } - + const CodePointArrayView getNthPrevWordCodePoints(const size_t n) const; // n is 1-indexed. - bool isNthPrevWordBeginningOfSentence(const size_t n) const { - if (n <= 0 || n > mPrevWordCount) { - return false; - } - return mIsBeginningOfSentence[n - 1]; - } + bool isNthPrevWordBeginningOfSentence(const size_t n) const; private: DISALLOW_ASSIGNMENT_OPERATOR(NgramContext); static int getWordId(const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, const int *const wordCodePoints, const int wordCodePointCount, - const bool isBeginningOfSentence, const bool tryLowerCaseSearch) { - if (!dictStructurePolicy || !wordCodePoints || wordCodePointCount > MAX_WORD_LENGTH) { - return NOT_A_WORD_ID; - } - int codePoints[MAX_WORD_LENGTH]; - int codePointCount = wordCodePointCount; - memmove(codePoints, wordCodePoints, sizeof(int) * codePointCount); - if (isBeginningOfSentence) { - codePointCount = CharUtils::attachBeginningOfSentenceMarker(codePoints, - codePointCount, MAX_WORD_LENGTH); - if (codePointCount <= 0) { - return NOT_A_WORD_ID; - } - } - const CodePointArrayView codePointArrayView(codePoints, codePointCount); - const int wordId = dictStructurePolicy->getWordId( - codePointArrayView, false /* forceLowerCaseSearch */); - if (wordId != NOT_A_WORD_ID || !tryLowerCaseSearch) { - // Return the id when when the word was found or doesn't try lower case search. - return wordId; - } - // Check bigrams for lower-cased previous word if original was not found. Useful for - // auto-capitalized words like "The [current_word]". - return dictStructurePolicy->getWordId(codePointArrayView, true /* forceLowerCaseSearch */); - } - - void clear() { - for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { - mPrevWordCodePointCount[i] = 0; - mIsBeginningOfSentence[i] = false; - } - } + const bool isBeginningOfSentence, const bool tryLowerCaseSearch); + void clear(); const size_t mPrevWordCount; int mPrevWordCodePoints[MAX_PREV_WORD_COUNT_FOR_N_GRAM][MAX_WORD_LENGTH]; diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index c71526293..c372d668b 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -160,8 +160,7 @@ void Suggest::expandCurrentDicNodes(DicTraverseSession *traverseSession) const { // TODO: Remove. Do not prune node here. const bool allowsErrorCorrections = TRAVERSAL->allowsErrorCorrections(&dicNode); // Process for handling space substitution (e.g., hevis => he is) - if (allowsErrorCorrections - && TRAVERSAL->isSpaceSubstitutionTerminal(traverseSession, &dicNode)) { + if (TRAVERSAL->isSpaceSubstitutionTerminal(traverseSession, &dicNode)) { createNextWordDicNode(traverseSession, &dicNode, true /* spaceSubstitution */); } @@ -417,6 +416,11 @@ void Suggest::createNextWordDicNode(DicTraverseSession *traverseSession, DicNode traverseSession->getDictionaryStructurePolicy()->getWordAttributesInContext( dicNode->getPrevWordIds(), dicNode->getWordId(), traverseSession->getMultiBigramMap()); + if (SuggestionsOutputUtils::shouldBlockWord(traverseSession->getSuggestOptions(), + dicNode, wordAttributes, false /* isLastWord */)) { + return; + } + if (!TRAVERSAL->isGoodToTraverseNextWord(dicNode, wordAttributes.getProbability())) { return; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp index 4c4dfc578..300e96c4e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp @@ -30,6 +30,7 @@ const char *const HeaderPolicy::DATE_KEY = "date"; const char *const HeaderPolicy::LAST_DECAYED_TIME_KEY = "LAST_DECAYED_TIME"; const char *const HeaderPolicy::UNIGRAM_COUNT_KEY = "UNIGRAM_COUNT"; const char *const HeaderPolicy::BIGRAM_COUNT_KEY = "BIGRAM_COUNT"; +const char *const HeaderPolicy::TRIGRAM_COUNT_KEY = "TRIGRAM_COUNT"; const char *const HeaderPolicy::EXTENDED_REGION_SIZE_KEY = "EXTENDED_REGION_SIZE"; // Historical info is information that is needed to support decaying such as timestamp, level and // count. @@ -38,15 +39,17 @@ const char *const HeaderPolicy::LOCALE_KEY = "locale"; // match Java declaration const char *const HeaderPolicy::FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY = "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID"; -const char *const HeaderPolicy::MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT"; -const char *const HeaderPolicy::MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT"; +const char *const HeaderPolicy::MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_ENTRY_COUNT"; +const char *const HeaderPolicy::MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_ENTRY_COUNT"; +const char *const HeaderPolicy::MAX_TRIGRAM_COUNT_KEY = "MAX_TRIGRAM_ENTRY_COUNT"; const int HeaderPolicy::DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE = 100; const float HeaderPolicy::MULTIPLE_WORD_COST_MULTIPLIER_SCALE = 100.0f; const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID = 3; const int HeaderPolicy::DEFAULT_MAX_UNIGRAM_COUNT = 10000; -const int HeaderPolicy::DEFAULT_MAX_BIGRAM_COUNT = 10000; +const int HeaderPolicy::DEFAULT_MAX_BIGRAM_COUNT = 30000; +const int HeaderPolicy::DEFAULT_MAX_TRIGRAM_COUNT = 30000; // Used for logging. Question mark is used to indicate that the key is not found. void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *outValue, @@ -92,12 +95,11 @@ bool HeaderPolicy::readRequiresGermanUmlautProcessing() const { } bool HeaderPolicy::fillInAndWriteHeaderToBuffer(const bool updatesLastDecayedTime, - const int unigramCount, const int bigramCount, - const int extendedRegionSize, BufferWithExtendableBuffer *const outBuffer) const { + const EntryCounts &entryCounts, const int extendedRegionSize, + BufferWithExtendableBuffer *const outBuffer) const { int writingPos = 0; DictionaryHeaderStructurePolicy::AttributeMap attributeMapToWrite(mAttributeMap); - fillInHeader(updatesLastDecayedTime, unigramCount, bigramCount, - extendedRegionSize, &attributeMapToWrite); + fillInHeader(updatesLastDecayedTime, entryCounts, extendedRegionSize, &attributeMapToWrite); if (!HeaderReadWriteUtils::writeDictionaryVersion(outBuffer, mDictFormatVersion, &writingPos)) { return false; @@ -124,11 +126,15 @@ bool HeaderPolicy::fillInAndWriteHeaderToBuffer(const bool updatesLastDecayedTim return true; } -void HeaderPolicy::fillInHeader(const bool updatesLastDecayedTime, const int unigramCount, - const int bigramCount, const int extendedRegionSize, +void HeaderPolicy::fillInHeader(const bool updatesLastDecayedTime, + const EntryCounts &entryCounts, const int extendedRegionSize, DictionaryHeaderStructurePolicy::AttributeMap *outAttributeMap) const { - HeaderReadWriteUtils::setIntAttribute(outAttributeMap, UNIGRAM_COUNT_KEY, unigramCount); - HeaderReadWriteUtils::setIntAttribute(outAttributeMap, BIGRAM_COUNT_KEY, bigramCount); + HeaderReadWriteUtils::setIntAttribute(outAttributeMap, UNIGRAM_COUNT_KEY, + entryCounts.getUnigramCount()); + HeaderReadWriteUtils::setIntAttribute(outAttributeMap, BIGRAM_COUNT_KEY, + entryCounts.getBigramCount()); + HeaderReadWriteUtils::setIntAttribute(outAttributeMap, TRIGRAM_COUNT_KEY, + entryCounts.getTrigramCount()); HeaderReadWriteUtils::setIntAttribute(outAttributeMap, EXTENDED_REGION_SIZE_KEY, extendedRegionSize); // Set the current time as the generation time. diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h index bc8eaded3..7a5acd7d5 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h @@ -22,6 +22,7 @@ #include "defines.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/policyimpl/dictionary/header/header_read_write_utils.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" #include "utils/char_utils.h" #include "utils/time_keeper.h" @@ -49,6 +50,8 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { UNIGRAM_COUNT_KEY, 0 /* defaultValue */)), mBigramCount(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, BIGRAM_COUNT_KEY, 0 /* defaultValue */)), + mTrigramCount(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, + TRIGRAM_COUNT_KEY, 0 /* defaultValue */)), mExtendedRegionSize(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, EXTENDED_REGION_SIZE_KEY, 0 /* defaultValue */)), mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue( @@ -60,6 +63,8 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { &mAttributeMap, MAX_UNIGRAM_COUNT_KEY, DEFAULT_MAX_UNIGRAM_COUNT)), mMaxBigramCount(HeaderReadWriteUtils::readIntAttributeValue( &mAttributeMap, MAX_BIGRAM_COUNT_KEY, DEFAULT_MAX_BIGRAM_COUNT)), + mMaxTrigramCount(HeaderReadWriteUtils::readIntAttributeValue( + &mAttributeMap, MAX_TRIGRAM_COUNT_KEY, DEFAULT_MAX_TRIGRAM_COUNT)), mCodePointTable(HeaderReadWriteUtils::readCodePointTable(&mAttributeMap)) {} // Constructs header information using an attribute map. @@ -77,7 +82,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { DATE_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)), mLastDecayedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, DATE_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)), - mUnigramCount(0), mBigramCount(0), mExtendedRegionSize(0), + mUnigramCount(0), mBigramCount(0), mTrigramCount(0), mExtendedRegionSize(0), mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue( &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)), mForgettingCurveProbabilityValuesTableId(HeaderReadWriteUtils::readIntAttributeValue( @@ -87,6 +92,8 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { &mAttributeMap, MAX_UNIGRAM_COUNT_KEY, DEFAULT_MAX_UNIGRAM_COUNT)), mMaxBigramCount(HeaderReadWriteUtils::readIntAttributeValue( &mAttributeMap, MAX_BIGRAM_COUNT_KEY, DEFAULT_MAX_BIGRAM_COUNT)), + mMaxTrigramCount(HeaderReadWriteUtils::readIntAttributeValue( + &mAttributeMap, MAX_TRIGRAM_COUNT_KEY, DEFAULT_MAX_TRIGRAM_COUNT)), mCodePointTable(HeaderReadWriteUtils::readCodePointTable(&mAttributeMap)) {} // Copy header information @@ -99,12 +106,14 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { mIsDecayingDict(headerPolicy->mIsDecayingDict), mDate(headerPolicy->mDate), mLastDecayedTime(headerPolicy->mLastDecayedTime), mUnigramCount(headerPolicy->mUnigramCount), mBigramCount(headerPolicy->mBigramCount), + mTrigramCount(headerPolicy->mTrigramCount), mExtendedRegionSize(headerPolicy->mExtendedRegionSize), mHasHistoricalInfoOfWords(headerPolicy->mHasHistoricalInfoOfWords), mForgettingCurveProbabilityValuesTableId( headerPolicy->mForgettingCurveProbabilityValuesTableId), mMaxUnigramCount(headerPolicy->mMaxUnigramCount), mMaxBigramCount(headerPolicy->mMaxBigramCount), + mMaxTrigramCount(headerPolicy->mMaxTrigramCount), mCodePointTable(headerPolicy->mCodePointTable) {} // Temporary dummy header. @@ -112,10 +121,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { : mDictFormatVersion(FormatUtils::UNKNOWN_VERSION), mDictionaryFlags(0), mSize(0), mAttributeMap(), mLocale(CharUtils::EMPTY_STRING), mMultiWordCostMultiplier(0.0f), mRequiresGermanUmlautProcessing(false), mIsDecayingDict(false), - mDate(0), mLastDecayedTime(0), mUnigramCount(0), mBigramCount(0), + mDate(0), mLastDecayedTime(0), mUnigramCount(0), mBigramCount(0), mTrigramCount(0), mExtendedRegionSize(0), mHasHistoricalInfoOfWords(false), mForgettingCurveProbabilityValuesTableId(0), mMaxUnigramCount(0), mMaxBigramCount(0), - mCodePointTable(nullptr) {} + mMaxTrigramCount(0), mCodePointTable(nullptr) {} ~HeaderPolicy() {} @@ -125,15 +134,17 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { // same so we use them for both here. switch (mDictFormatVersion) { case FormatUtils::VERSION_2: - return FormatUtils::VERSION_2; case FormatUtils::VERSION_201: - return FormatUtils::VERSION_201; + AKLOGE("Dictionary versions 2 and 201 are incompatible with this version"); + return FormatUtils::UNKNOWN_VERSION; + case FormatUtils::VERSION_202: + return FormatUtils::VERSION_202; case FormatUtils::VERSION_4_ONLY_FOR_TESTING: return FormatUtils::VERSION_4_ONLY_FOR_TESTING; - case FormatUtils::VERSION_4: - return FormatUtils::VERSION_4; - case FormatUtils::VERSION_4_DEV: - return FormatUtils::VERSION_4_DEV; + case FormatUtils::VERSION_402: + return FormatUtils::VERSION_402; + case FormatUtils::VERSION_403: + return FormatUtils::VERSION_403; default: return FormatUtils::UNKNOWN_VERSION; } @@ -183,6 +194,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { return mBigramCount; } + AK_FORCE_INLINE int getTrigramCount() const { + return mTrigramCount; + } + AK_FORCE_INLINE int getExtendedRegionSize() const { return mExtendedRegionSize; } @@ -212,15 +227,19 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { return mMaxBigramCount; } + AK_FORCE_INLINE int getMaxTrigramCount() const { + return mMaxTrigramCount; + } + void readHeaderValueOrQuestionMark(const char *const key, int *outValue, int outValueSize) const; bool fillInAndWriteHeaderToBuffer(const bool updatesLastDecayedTime, - const int unigramCount, const int bigramCount, - const int extendedRegionSize, BufferWithExtendableBuffer *const outBuffer) const; + const EntryCounts &entryCounts, const int extendedRegionSize, + BufferWithExtendableBuffer *const outBuffer) const; - void fillInHeader(const bool updatesLastDecayedTime, - const int unigramCount, const int bigramCount, const int extendedRegionSize, + void fillInHeader(const bool updatesLastDecayedTime, const EntryCounts &entryCounts, + const int extendedRegionSize, DictionaryHeaderStructurePolicy::AttributeMap *outAttributeMap) const; AK_FORCE_INLINE const std::vector<int> *getLocale() const { @@ -228,7 +247,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { } bool supportsBeginningOfSentence() const { - return mDictFormatVersion >= FormatUtils::VERSION_4; + return mDictFormatVersion >= FormatUtils::VERSION_402; } const int *getCodePointTable() const { @@ -245,6 +264,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { static const char *const LAST_DECAYED_TIME_KEY; static const char *const UNIGRAM_COUNT_KEY; static const char *const BIGRAM_COUNT_KEY; + static const char *const TRIGRAM_COUNT_KEY; static const char *const EXTENDED_REGION_SIZE_KEY; static const char *const HAS_HISTORICAL_INFO_KEY; static const char *const LOCALE_KEY; @@ -253,11 +273,13 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { static const char *const FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY; static const char *const MAX_UNIGRAM_COUNT_KEY; static const char *const MAX_BIGRAM_COUNT_KEY; + static const char *const MAX_TRIGRAM_COUNT_KEY; static const int DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE; static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE; static const int DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID; static const int DEFAULT_MAX_UNIGRAM_COUNT; static const int DEFAULT_MAX_BIGRAM_COUNT; + static const int DEFAULT_MAX_TRIGRAM_COUNT; const FormatUtils::FORMAT_VERSION mDictFormatVersion; const HeaderReadWriteUtils::DictionaryFlags mDictionaryFlags; @@ -271,11 +293,13 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { const int mLastDecayedTime; const int mUnigramCount; const int mBigramCount; + const int mTrigramCount; const int mExtendedRegionSize; const bool mHasHistoricalInfoOfWords; const int mForgettingCurveProbabilityValuesTableId; const int mMaxUnigramCount; const int mMaxBigramCount; + const int mMaxTrigramCount; const int *const mCodePointTable; const std::vector<int> readLocale() const; diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp index 41a8b13b8..19ed0d468 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp @@ -111,11 +111,12 @@ typedef DictionaryHeaderStructurePolicy::AttributeMap AttributeMap; switch (version) { case FormatUtils::VERSION_2: case FormatUtils::VERSION_201: - // Version 2 or 201 dictionary writing is not supported. + case FormatUtils::VERSION_202: + // None of the static dictionaries (v2x) support writing return false; case FormatUtils::VERSION_4_ONLY_FOR_TESTING: - case FormatUtils::VERSION_4: - case FormatUtils::VERSION_4_DEV: + case FormatUtils::VERSION_402: + case FormatUtils::VERSION_403: return buffer->writeUintAndAdvancePosition(version /* data */, HEADER_DICTIONARY_VERSION_SIZE, writingPos); default: diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp index 9e1adff70..15ac88319 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp @@ -65,6 +65,8 @@ const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition( (encodedTargetTerminalId == Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID) ? Ver4DictConstants::NOT_A_TERMINAL_ID : encodedTargetTerminalId; if (mHasHistoricalInfo) { + // Hack for better migration. + count += level; const HistoricalInfo historicalInfo(timestamp, level, count); return BigramEntry(hasNext, probability, &historicalInfo, targetTerminalId); } else { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp index ef6166ffd..61ef4aa42 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp @@ -50,7 +50,8 @@ const ProbabilityEntry ProbabilityDictContent::getProbabilityEntry(const int ter Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &entryPos); const int count = buffer->readUintAndAdvancePosition( Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &entryPos); - const HistoricalInfo historicalInfo(timestamp, level, count); + // Hack for better migration. + const HistoricalInfo historicalInfo(timestamp, level, count + level); return ProbabilityEntry(flags, probability, &historicalInfo); } else { return ProbabilityEntry(flags, probability); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp index 6243f14cc..d558b949a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp @@ -245,7 +245,7 @@ bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds if (!sourcePtNodeParams.hasBigrams()) { // Update has bigrams flag. return updatePtNodeFlags(sourcePtNodeParams.getHeadPos(), - sourcePtNodeParams.isBlacklisted(), sourcePtNodeParams.isNotAWord(), + sourcePtNodeParams.isPossiblyOffensive(), sourcePtNodeParams.isNotAWord(), sourcePtNodeParams.isTerminal(), sourcePtNodeParams.hasShortcutTargets(), true /* hasBigrams */, sourcePtNodeParams.getCodePointCount() > 1 /* hasMultipleChars */); @@ -316,7 +316,7 @@ bool Ver4PatriciaTrieNodeWriter::addShortcutTarget(const PtNodeParams *const ptN if (!ptNodeParams->hasShortcutTargets()) { // Update has shortcut targets flag. return updatePtNodeFlags(ptNodeParams->getHeadPos(), - ptNodeParams->isBlacklisted(), ptNodeParams->isNotAWord(), + ptNodeParams->isPossiblyOffensive(), ptNodeParams->isNotAWord(), ptNodeParams->isTerminal(), true /* hasShortcutTargets */, ptNodeParams->hasBigrams(), ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); @@ -330,7 +330,7 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeHasBigramsAndShortcutTargetsFlags( ptNodeParams->getTerminalId()) != NOT_A_DICT_POS; const bool hasShortcutTargets = mBuffers->getShortcutDictContent()->getShortcutListHeadPos( ptNodeParams->getTerminalId()) != NOT_A_DICT_POS; - return updatePtNodeFlags(ptNodeParams->getHeadPos(), ptNodeParams->isBlacklisted(), + return updatePtNodeFlags(ptNodeParams->getHeadPos(), ptNodeParams->isPossiblyOffensive(), ptNodeParams->isNotAWord(), ptNodeParams->isTerminal(), hasShortcutTargets, hasBigrams, ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); } @@ -386,8 +386,9 @@ bool Ver4PatriciaTrieNodeWriter::writePtNodeAndGetTerminalIdAndAdvancePosition( ptNodeParams->getChildrenPos(), ptNodeWritingPos)) { return false; } - return updatePtNodeFlags(nodePos, ptNodeParams->isBlacklisted(), ptNodeParams->isNotAWord(), - isTerminal, ptNodeParams->hasShortcutTargets(), ptNodeParams->hasBigrams(), + return updatePtNodeFlags(nodePos, ptNodeParams->isPossiblyOffensive(), + ptNodeParams->isNotAWord(), isTerminal, ptNodeParams->hasShortcutTargets(), + ptNodeParams->hasBigrams(), ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp index 0eae934ae..9455222dd 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp @@ -140,7 +140,7 @@ const WordAttributes Ver4PatriciaTriePolicy::getWordAttributesInContext( const WordAttributes Ver4PatriciaTriePolicy::getWordAttributes(const int probability, const PtNodeParams &ptNodeParams) const { - return WordAttributes(probability, ptNodeParams.isBlacklisted(), ptNodeParams.isNotAWord(), + return WordAttributes(probability, false /* isBlacklisted */, ptNodeParams.isNotAWord(), ptNodeParams.getProbability() == 0); } @@ -164,7 +164,7 @@ int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordI } const int ptNodePos = getTerminalPtNodePosFromWordId(wordId); const PtNodeParams ptNodeParams(mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos)); - if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) { + if (ptNodeParams.isDeleted() || ptNodeParams.isNotAWord()) { return NOT_A_PROBABILITY; } if (prevWordIds.empty()) { @@ -303,7 +303,7 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const CodePointArrayView wordCodePo if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointArrayView, unigramProperty, &addedNewUnigram)) { if (addedNewUnigram && !unigramProperty->representsBeginningOfSentence()) { - mUnigramCount++; + mEntryCounters.incrementUnigramCount(); } if (unigramProperty->getShortcuts().size() > 0) { // Add shortcut target. @@ -344,8 +344,7 @@ bool Ver4PatriciaTriePolicy::removeUnigramEntry(const CodePointArrayView wordCod return mNodeWriter.suppressUnigramEntry(&ptNodeParams); } -bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty) { +bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramProperty *const ngramProperty) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); return false; @@ -355,6 +354,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContex mDictBuffer->getTailPosition()); return false; } + const NgramContext *const ngramContext = ngramProperty->getNgramContext(); if (!ngramContext->isValid()) { AKLOGE("Ngram context is not valid for adding n-gram entry to the dictionary."); return false; @@ -397,7 +397,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContex if (mUpdatingHelper.addNgramEntry(PtNodePosArrayView::singleElementView(&prevWordPtNodePos), wordPos, ngramProperty, &addedNewBigram)) { if (addedNewBigram) { - mBigramCount++; + mEntryCounters.incrementBigramCount(); } return true; } else { @@ -438,7 +438,7 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const NgramContext *const ngramCon const int prevWordPtNodePos = getTerminalPtNodePosFromWordId(prevWordIds[0]); if (mUpdatingHelper.removeNgramEntry( PtNodePosArrayView::singleElementView(&prevWordPtNodePos), wordPos)) { - mBigramCount--; + mEntryCounters.decrementBigramCount(); return true; } else { return false; @@ -463,9 +463,9 @@ bool Ver4PatriciaTriePolicy::updateEntriesForWordWithNgramContext( } const int probabilityForNgram = ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */) ? NOT_A_PROBABILITY : probability; - const NgramProperty ngramProperty(wordCodePoints.toVector(), probabilityForNgram, + const NgramProperty ngramProperty(*ngramContext, wordCodePoints.toVector(), probabilityForNgram, historicalInfo); - if (!addNgramEntry(ngramContext, &ngramProperty)) { + if (!addNgramEntry(&ngramProperty)) { AKLOGE("Cannot update unigarm entry in updateEntriesForWordWithNgramContext()."); return false; } @@ -477,7 +477,7 @@ bool Ver4PatriciaTriePolicy::flush(const char *const filePath) { AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath); return false; } - if (!mWritingHelper.writeToDictFile(filePath, mUnigramCount, mBigramCount)) { + if (!mWritingHelper.writeToDictFile(filePath, mEntryCounters.getEntryCounts())) { AKLOGE("Cannot flush the dictionary to file."); mIsCorrupted = true; return false; @@ -515,7 +515,7 @@ bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { // Needs to reduce dictionary size. return true; } else if (mHeaderPolicy->isDecayingDict()) { - return ForgettingCurveUtils::needsToDecay(mindsBlockByGC, mUnigramCount, mBigramCount, + return ForgettingCurveUtils::needsToDecay(mindsBlockByGC, mEntryCounters.getEntryCounts(), mHeaderPolicy); } return false; @@ -525,19 +525,19 @@ void Ver4PatriciaTriePolicy::getProperty(const char *const query, const int quer char *const outResult, const int maxResultLength) { const int compareLength = queryLength + 1 /* terminator */; if (strncmp(query, UNIGRAM_COUNT_QUERY, compareLength) == 0) { - snprintf(outResult, maxResultLength, "%d", mUnigramCount); + snprintf(outResult, maxResultLength, "%d", mEntryCounters.getUnigramCount()); } else if (strncmp(query, BIGRAM_COUNT_QUERY, compareLength) == 0) { - snprintf(outResult, maxResultLength, "%d", mBigramCount); + snprintf(outResult, maxResultLength, "%d", mEntryCounters.getBigramCount()); } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, compareLength) == 0) { snprintf(outResult, maxResultLength, "%d", mHeaderPolicy->isDecayingDict() ? - ForgettingCurveUtils::getUnigramCountHardLimit( + ForgettingCurveUtils::getEntryCountHardLimit( mHeaderPolicy->getMaxUnigramCount()) : static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, compareLength) == 0) { snprintf(outResult, maxResultLength, "%d", mHeaderPolicy->isDecayingDict() ? - ForgettingCurveUtils::getBigramCountHardLimit( + ForgettingCurveUtils::getEntryCountHardLimit( mHeaderPolicy->getMaxBigramCount()) : static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); } @@ -580,11 +580,15 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty( getWordIdFromTerminalPtNodePos(word1TerminalPtNodePos), MAX_WORD_LENGTH, bigramWord1CodePoints); const HistoricalInfo *const historicalInfo = bigramEntry.getHistoricalInfo(); - const int probability = bigramEntry.hasHistoricalInfo() ? - ForgettingCurveUtils::decodeProbability( - bigramEntry.getHistoricalInfo(), mHeaderPolicy) : - bigramEntry.getProbability(); + const int rawBigramProbability = bigramEntry.hasHistoricalInfo() + ? ForgettingCurveUtils::decodeProbability( + bigramEntry.getHistoricalInfo(), mHeaderPolicy) + : bigramEntry.getProbability(); + const int probability = getBigramConditionalProbability(ptNodeParams.getProbability(), + ptNodeParams.representsBeginningOfSentence(), rawBigramProbability); ngrams.emplace_back( + NgramContext(wordCodePoints.data(), wordCodePoints.size(), + ptNodeParams.representsBeginningOfSentence()), CodePointArrayView(bigramWord1CodePoints, codePointCount).toVector(), probability, *historicalInfo); } @@ -608,8 +612,8 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty( } } const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(), - ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(), - *historicalInfo, std::move(shortcuts)); + ptNodeParams.isNotAWord(), ptNodeParams.isPossiblyOffensive(), + ptNodeParams.getProbability(), *historicalInfo, std::move(shortcuts)); return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h index 1ad5e7e36..0480876ed 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h @@ -41,6 +41,7 @@ #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h" #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" #include "utils/int_array_view.h" namespace latinime { @@ -75,8 +76,8 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { &mPtNodeArrayReader, &mBigramPolicy, &mShortcutPolicy), mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter), mWritingHelper(mBuffers.get()), - mUnigramCount(mHeaderPolicy->getUnigramCount()), - mBigramCount(mHeaderPolicy->getBigramCount()), + mEntryCounters(mHeaderPolicy->getUnigramCount(), mHeaderPolicy->getBigramCount(), + mHeaderPolicy->getTrigramCount()), mTerminalPtNodePositionsForIteratingWords(), mIsCorrupted(false) {}; virtual int getRootPosition() const { @@ -112,8 +113,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { bool removeUnigramEntry(const CodePointArrayView wordCodePoints); - bool addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty); + bool addNgramEntry(const NgramProperty *const ngramProperty); bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints); @@ -163,8 +163,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { Ver4PatriciaTrieNodeWriter mNodeWriter; DynamicPtUpdatingHelper mUpdatingHelper; Ver4PatriciaTrieWritingHelper mWritingHelper; - int mUnigramCount; - int mBigramCount; + MutableEntryCounters mEntryCounters; std::vector<int> mTerminalPtNodePositionsForIteratingWords; mutable bool mIsCorrupted; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp index 2887dc6b1..a033d396b 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp @@ -43,18 +43,18 @@ namespace backward { namespace v402 { bool Ver4PatriciaTrieWritingHelper::writeToDictFile(const char *const dictDirPath, - const int unigramCount, const int bigramCount) const { + const EntryCounts &entryCounts) const { const HeaderPolicy *const headerPolicy = mBuffers->getHeaderPolicy(); BufferWithExtendableBuffer headerBuffer( BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); const int extendedRegionSize = headerPolicy->getExtendedRegionSize() + mBuffers->getTrieBuffer()->getUsedAdditionalBufferSize(); if (!headerPolicy->fillInAndWriteHeaderToBuffer(false /* updatesLastDecayedTime */, - unigramCount, bigramCount, extendedRegionSize, &headerBuffer)) { + entryCounts, extendedRegionSize, &headerBuffer)) { AKLOGE("Cannot write header structure to buffer. " "updatesLastDecayedTime: %d, unigramCount: %d, bigramCount: %d, " - "extendedRegionSize: %d", false, unigramCount, bigramCount, - extendedRegionSize); + "extendedRegionSize: %d", false, entryCounts.getUnigramCount(), + entryCounts.getBigramCount(), extendedRegionSize); return false; } return mBuffers->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); @@ -74,7 +74,8 @@ bool Ver4PatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeAr BufferWithExtendableBuffer headerBuffer( BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); if (!headerPolicy->fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */, - unigramCount, bigramCount, 0 /* extendedRegionSize */, &headerBuffer)) { + EntryCounts(unigramCount, bigramCount, 0 /* trigramCount */), + 0 /* extendedRegionSize */, &headerBuffer)) { return false; } return dictBuffers->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h index 9034ee656..1aad33e38 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h @@ -27,6 +27,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.h" #include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" namespace latinime { namespace backward { @@ -46,8 +47,7 @@ class Ver4PatriciaTrieWritingHelper { Ver4PatriciaTrieWritingHelper(Ver4DictBuffers *const buffers) : mBuffers(buffers) {} - bool writeToDictFile(const char *const dictDirPath, const int unigramCount, - const int bigramCount) const; + bool writeToDictFile(const char *const dictDirPath, const EntryCounts &entryCounts) const; // This method cannot be const because the original dictionary buffer will be updated to detect // useless PtNodes during GC. diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp index 372c9e36f..9a9a21b6b 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp @@ -58,7 +58,7 @@ namespace latinime { const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { FormatUtils::FORMAT_VERSION dictFormatVersion = FormatUtils::getFormatVersion(formatVersion); switch (dictFormatVersion) { - case FormatUtils::VERSION_4: { + case FormatUtils::VERSION_402: { return newPolicyForOnMemoryV4Dict<backward::v402::Ver4DictConstants, backward::v402::Ver4DictBuffers, backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr, @@ -66,7 +66,7 @@ namespace latinime { dictFormatVersion, locale, attributeMap); } case FormatUtils::VERSION_4_ONLY_FOR_TESTING: - case FormatUtils::VERSION_4_DEV: { + case FormatUtils::VERSION_403: { return newPolicyForOnMemoryV4Dict<Ver4DictConstants, Ver4DictBuffers, Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>( dictFormatVersion, locale, attributeMap); @@ -115,9 +115,10 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str switch (formatVersion) { case FormatUtils::VERSION_2: case FormatUtils::VERSION_201: - AKLOGE("Given path is a directory but the format is version 2 or 201. path: %s", path); + case FormatUtils::VERSION_202: + AKLOGE("Given path is a directory but the format is version 2xx. path: %s", path); break; - case FormatUtils::VERSION_4: { + case FormatUtils::VERSION_402: { return newPolicyForV4Dict<backward::v402::Ver4DictConstants, backward::v402::Ver4DictBuffers, backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr, @@ -125,7 +126,7 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str headerFilePath, formatVersion, std::move(mmappedBuffer)); } case FormatUtils::VERSION_4_ONLY_FOR_TESTING: - case FormatUtils::VERSION_4_DEV: { + case FormatUtils::VERSION_403: { return newPolicyForV4Dict<Ver4DictConstants, Ver4DictBuffers, Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>( headerFilePath, formatVersion, std::move(mmappedBuffer)); @@ -177,11 +178,14 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str switch (FormatUtils::detectFormatVersion(mmappedBuffer->getReadOnlyByteArrayView())) { case FormatUtils::VERSION_2: case FormatUtils::VERSION_201: + AKLOGE("Dictionary versions 2 and 201 are incompatible with this version"); + break; + case FormatUtils::VERSION_202: return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( new PatriciaTriePolicy(std::move(mmappedBuffer))); case FormatUtils::VERSION_4_ONLY_FOR_TESTING: - case FormatUtils::VERSION_4: - case FormatUtils::VERSION_4_DEV: + case FormatUtils::VERSION_402: + case FormatUtils::VERSION_403: AKLOGE("Given path is a file but the format is version 4. path: %s", path); break; default: diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp index 92fd6f214..e524e86e5 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp @@ -146,7 +146,7 @@ bool DynamicPtUpdatingHelper::setPtNodeProbability(const PtNodeParams *const ori const int movedPos = mBuffer->getTailPosition(); int writingPos = movedPos; const PtNodeParams ptNodeParamsToWrite(getUpdatedPtNodeParams(originalPtNodeParams, - unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), + unigramProperty->isNotAWord(), unigramProperty->isPossiblyOffensive(), true /* isTerminal */, originalPtNodeParams->getParentPos(), originalPtNodeParams->getCodePointArrayView(), unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, @@ -180,8 +180,9 @@ bool DynamicPtUpdatingHelper::createNewPtNodeArrayWithAChildPtNode( return false; } const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( - unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), true /* isTerminal */, - parentPtNodePos, ptNodeCodePoints, unigramProperty->getProbability())); + unigramProperty->isNotAWord(), unigramProperty->isPossiblyOffensive(), + true /* isTerminal */, parentPtNodePos, ptNodeCodePoints, + unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, unigramProperty, &writingPos)) { return false; @@ -214,7 +215,7 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( reallocatingPtNodeParams->getCodePointArrayView().limit(overlappingCodePointCount); if (addsExtraChild) { const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( - false /* isNotAWord */, false /* isBlacklisted */, false /* isTerminal */, + false /* isNotAWord */, false /* isPossiblyOffensive */, false /* isTerminal */, reallocatingPtNodeParams->getParentPos(), firstPtNodeCodePoints, NOT_A_PROBABILITY)); if (!mPtNodeWriter->writePtNodeAndAdvancePosition(&ptNodeParamsToWrite, &writingPos)) { @@ -222,7 +223,7 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( } } else { const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( - unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), + unigramProperty->isNotAWord(), unigramProperty->isPossiblyOffensive(), true /* isTerminal */, reallocatingPtNodeParams->getParentPos(), firstPtNodeCodePoints, unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, @@ -240,7 +241,7 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( // Write the 2nd part of the reallocating node. const int secondPartOfReallocatedPtNodePos = writingPos; const PtNodeParams childPartPtNodeParams(getUpdatedPtNodeParams(reallocatingPtNodeParams, - reallocatingPtNodeParams->isNotAWord(), reallocatingPtNodeParams->isBlacklisted(), + reallocatingPtNodeParams->isNotAWord(), reallocatingPtNodeParams->isPossiblyOffensive(), reallocatingPtNodeParams->isTerminal(), firstPartOfReallocatedPtNodePos, reallocatingPtNodeParams->getCodePointArrayView().skip(overlappingCodePointCount), reallocatingPtNodeParams->getProbability())); @@ -249,7 +250,7 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( } if (addsExtraChild) { const PtNodeParams extraChildPtNodeParams(getPtNodeParamsForNewPtNode( - unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), + unigramProperty->isNotAWord(), unigramProperty->isPossiblyOffensive(), true /* isTerminal */, firstPartOfReallocatedPtNodePos, newPtNodeCodePoints.skip(overlappingCodePointCount), unigramProperty->getProbability())); @@ -276,20 +277,20 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( const PtNodeParams DynamicPtUpdatingHelper::getUpdatedPtNodeParams( const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, - const bool isBlacklisted, const bool isTerminal, const int parentPos, + const bool isPossiblyOffensive, const bool isTerminal, const int parentPos, const CodePointArrayView codePoints, const int probability) const { const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::createAndGetFlags( - isBlacklisted, isNotAWord, isTerminal, false /* hasShortcutTargets */, + isPossiblyOffensive, isNotAWord, isTerminal, false /* hasShortcutTargets */, false /* hasBigrams */, codePoints.size() > 1u /* hasMultipleChars */, CHILDREN_POSITION_FIELD_SIZE); return PtNodeParams(originalPtNodeParams, flags, parentPos, codePoints, probability); } const PtNodeParams DynamicPtUpdatingHelper::getPtNodeParamsForNewPtNode(const bool isNotAWord, - const bool isBlacklisted, const bool isTerminal, const int parentPos, + const bool isPossiblyOffensive, const bool isTerminal, const int parentPos, const CodePointArrayView codePoints, const int probability) const { const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::createAndGetFlags( - isBlacklisted, isNotAWord, isTerminal, false /* hasShortcutTargets */, + isPossiblyOffensive, isNotAWord, isTerminal, false /* hasShortcutTargets */, false /* hasBigrams */, codePoints.size() > 1u /* hasMultipleChars */, CHILDREN_POSITION_FIELD_SIZE); return PtNodeParams(flags, parentPos, codePoints, probability); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h index 2bbe2f4dc..db5f6ab17 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h @@ -85,12 +85,12 @@ class DynamicPtUpdatingHelper { const CodePointArrayView newPtNodeCodePoints); const PtNodeParams getUpdatedPtNodeParams(const PtNodeParams *const originalPtNodeParams, - const bool isNotAWord, const bool isBlacklisted, const bool isTerminal, + const bool isNotAWord, const bool isPossiblyOffensive, const bool isTerminal, const int parentPos, const CodePointArrayView codePoints, const int probability) const; - const PtNodeParams getPtNodeParamsForNewPtNode(const bool isNotAWord, const bool isBlacklisted, - const bool isTerminal, const int parentPos, const CodePointArrayView codePoints, - const int probability) const; + const PtNodeParams getPtNodeParamsForNewPtNode(const bool isNotAWord, + const bool isPossiblyOffensive, const bool isTerminal, const int parentPos, + const CodePointArrayView codePoints, const int probability) const; }; } // namespace latinime #endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_UPDATING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.cpp index 6a498b2f4..b8d78bf10 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.cpp @@ -41,8 +41,8 @@ const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_SHORTCUT_TARGETS = 0x08 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_BIGRAMS = 0x04; // Flag for non-words (typically, shortcut only entries) const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_NOT_A_WORD = 0x02; -// Flag for blacklist -const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_BLACKLISTED = 0x01; +// Flag for possibly offensive words +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_POSSIBLY_OFFENSIVE = 0x01; /* static */ int PtReadingUtils::getPtNodeArraySizeAndAdvancePosition( const uint8_t *const buffer, int *const pos) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h index a69ec4435..6a2bf5d3c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h @@ -54,8 +54,8 @@ class PatriciaTrieReadingUtils { /** * Node Flags */ - static AK_FORCE_INLINE bool isBlacklisted(const NodeFlags flags) { - return (flags & FLAG_IS_BLACKLISTED) != 0; + static AK_FORCE_INLINE bool isPossiblyOffensive(const NodeFlags flags) { + return (flags & FLAG_IS_POSSIBLY_OFFENSIVE) != 0; } static AK_FORCE_INLINE bool isNotAWord(const NodeFlags flags) { @@ -82,12 +82,12 @@ class PatriciaTrieReadingUtils { return FLAG_CHILDREN_POSITION_TYPE_NOPOSITION != (MASK_CHILDREN_POSITION_TYPE & flags); } - static AK_FORCE_INLINE NodeFlags createAndGetFlags(const bool isBlacklisted, + static AK_FORCE_INLINE NodeFlags createAndGetFlags(const bool isPossiblyOffensive, const bool isNotAWord, const bool isTerminal, const bool hasShortcutTargets, const bool hasBigrams, const bool hasMultipleChars, const int childrenPositionFieldSize) { NodeFlags nodeFlags = 0; - nodeFlags = isBlacklisted ? (nodeFlags | FLAG_IS_BLACKLISTED) : nodeFlags; + nodeFlags = isPossiblyOffensive ? (nodeFlags | FLAG_IS_POSSIBLY_OFFENSIVE) : nodeFlags; nodeFlags = isNotAWord ? (nodeFlags | FLAG_IS_NOT_A_WORD) : nodeFlags; nodeFlags = isTerminal ? (nodeFlags | FLAG_IS_TERMINAL) : nodeFlags; nodeFlags = hasShortcutTargets ? (nodeFlags | FLAG_HAS_SHORTCUT_TARGETS) : nodeFlags; @@ -127,7 +127,7 @@ class PatriciaTrieReadingUtils { static const NodeFlags FLAG_HAS_SHORTCUT_TARGETS; static const NodeFlags FLAG_HAS_BIGRAMS; static const NodeFlags FLAG_IS_NOT_A_WORD; - static const NodeFlags FLAG_IS_BLACKLISTED; + static const NodeFlags FLAG_IS_POSSIBLY_OFFENSIVE; }; } // namespace latinime #endif /* LATINIME_PATRICIA_TRIE_NODE_READING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h index 3ff1829bd..e52706e07 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h @@ -144,8 +144,8 @@ class PtNodeParams { return PatriciaTrieReadingUtils::isTerminal(mFlags); } - AK_FORCE_INLINE bool isBlacklisted() const { - return PatriciaTrieReadingUtils::isBlacklisted(mFlags); + AK_FORCE_INLINE bool isPossiblyOffensive() const { + return PatriciaTrieReadingUtils::isPossiblyOffensive(mFlags); } AK_FORCE_INLINE bool isNotAWord() const { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp index b7f1199c5..59873612a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ - #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h" #include "defines.h" @@ -317,8 +316,8 @@ const WordAttributes PatriciaTriePolicy::getWordAttributesInContext( const WordAttributes PatriciaTriePolicy::getWordAttributes(const int probability, const PtNodeParams &ptNodeParams) const { - return WordAttributes(probability, ptNodeParams.isBlacklisted(), ptNodeParams.isNotAWord(), - ptNodeParams.getProbability() == 0); + return WordAttributes(probability, false /* isBlacklisted */, ptNodeParams.isNotAWord(), + ptNodeParams.isPossiblyOffensive()); } int PatriciaTriePolicy::getProbability(const int unigramProbability, @@ -345,10 +344,9 @@ int PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordIds, const int ptNodePos = getTerminalPtNodePosFromWordId(wordId); const PtNodeParams ptNodeParams = mPtNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos); - if (ptNodeParams.isNotAWord() || ptNodeParams.isBlacklisted()) { - // If this is not a word, or if it's a blacklisted entry, it should behave as - // having no probability outside of the suggestion process (where it should be used - // for shortcuts). + if (ptNodeParams.isNotAWord()) { + // If this is not a word, it should behave as having no probability outside of the + // suggestion process (where it should be used for shortcuts). return NOT_A_PROBABILITY; } if (!prevWordIds.empty()) { @@ -451,6 +449,8 @@ const WordProperty PatriciaTriePolicy::getWordProperty( bigramWord1CodePoints, &word1Probability); const int probability = getProbability(word1Probability, bigramsIt.getProbability()); ngrams.emplace_back( + NgramContext(wordCodePoints.data(), wordCodePoints.size(), + ptNodeParams.representsBeginningOfSentence()), CodePointArrayView(bigramWord1CodePoints, word1CodePointCount).toVector(), probability, HistoricalInfo()); } @@ -476,8 +476,8 @@ const WordProperty PatriciaTriePolicy::getWordProperty( } } const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(), - ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(), - HistoricalInfo(), std::move(shortcuts)); + ptNodeParams.isNotAWord(), ptNodeParams.isPossiblyOffensive(), + ptNodeParams.getProbability(), HistoricalInfo(), std::move(shortcuts)); return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h index b17681388..8933962ab 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h @@ -93,8 +93,7 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { return false; } - bool addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty) { + bool addNgramEntry(const NgramProperty *const ngramProperty) { // This method should not be called for non-updatable dictionary. AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); return false; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.cpp new file mode 100644 index 000000000..b0fbb3e72 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.cpp @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#include "suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h" + +namespace latinime { + +// These counts are used to provide stable probabilities even if the user's input count is small. +const int DynamicLanguageModelProbabilityUtils::ASSUMED_MIN_COUNT_FOR_UNIGRAMS = 8192; +const int DynamicLanguageModelProbabilityUtils::ASSUMED_MIN_COUNT_FOR_BIGRAMS = 2; +const int DynamicLanguageModelProbabilityUtils::ASSUMED_MIN_COUNT_FOR_TRIGRAMS = 2; + +// These are encoded backoff weights. +// Note that we give positive value for trigrams that means the weight is more than 1. +// TODO: Apply backoff for main dictionaries and quit giving a positive backoff weight. +const int DynamicLanguageModelProbabilityUtils::ENCODED_BACKOFF_WEIGHT_FOR_UNIGRAMS = -32; +const int DynamicLanguageModelProbabilityUtils::ENCODED_BACKOFF_WEIGHT_FOR_BIGRAMS = 0; +const int DynamicLanguageModelProbabilityUtils::ENCODED_BACKOFF_WEIGHT_FOR_TRIGRAMS = 8; + +// This value is used to remove too old entries from the dictionary. +const int DynamicLanguageModelProbabilityUtils::DURATION_TO_DISCARD_ENTRY_IN_SECONDS = + 300 * 24 * 60 * 60; // 300 days + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h new file mode 100644 index 000000000..88bc58fe8 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef LATINIME_DYNAMIC_LANGUAGE_MODEL_PROBABILITY_UTILS_H +#define LATINIME_DYNAMIC_LANGUAGE_MODEL_PROBABILITY_UTILS_H + +#include <algorithm> + +#include "defines.h" +#include "suggest/core/dictionary/property/historical_info.h" +#include "utils/time_keeper.h" + +namespace latinime { + +class DynamicLanguageModelProbabilityUtils { + public: + static float computeRawProbabilityFromCounts(const int count, const int contextCount, + const int matchedWordCountInContext) { + int minCount = 0; + switch (matchedWordCountInContext) { + case 1: + minCount = ASSUMED_MIN_COUNT_FOR_UNIGRAMS; + break; + case 2: + minCount = ASSUMED_MIN_COUNT_FOR_BIGRAMS; + break; + case 3: + minCount = ASSUMED_MIN_COUNT_FOR_TRIGRAMS; + break; + default: + AKLOGE("computeRawProbabilityFromCounts is called with invalid " + "matchedWordCountInContext (%d).", matchedWordCountInContext); + ASSERT(false); + return 0.0f; + } + return static_cast<float>(count) / static_cast<float>(std::max(contextCount, minCount)); + } + + static float backoff(const int ngramProbability, const int matchedWordCountInContext) { + int probability = NOT_A_PROBABILITY; + + switch (matchedWordCountInContext) { + case 1: + probability = ngramProbability + ENCODED_BACKOFF_WEIGHT_FOR_UNIGRAMS; + break; + case 2: + probability = ngramProbability + ENCODED_BACKOFF_WEIGHT_FOR_BIGRAMS; + break; + case 3: + probability = ngramProbability + ENCODED_BACKOFF_WEIGHT_FOR_TRIGRAMS; + break; + default: + AKLOGE("backoff is called with invalid matchedWordCountInContext (%d).", + matchedWordCountInContext); + ASSERT(false); + return NOT_A_PROBABILITY; + } + return std::min(std::max(probability, NOT_A_PROBABILITY), MAX_PROBABILITY); + } + + static int getDecayedProbability(const int probability, const HistoricalInfo historicalInfo) { + const int elapsedTime = TimeKeeper::peekCurrentTime() - historicalInfo.getTimestamp(); + if (elapsedTime < 0) { + AKLOGE("The elapsed time is negatime value. Timestamp overflow?"); + return NOT_A_PROBABILITY; + } + // TODO: Improve this logic. + // We don't modify probability depending on the elapsed time. + return probability; + } + + static int shouldRemoveEntryDuringGC(const HistoricalInfo historicalInfo) { + // TODO: Improve this logic. + const int elapsedTime = TimeKeeper::peekCurrentTime() - historicalInfo.getTimestamp(); + return elapsedTime > DURATION_TO_DISCARD_ENTRY_IN_SECONDS; + } + + static int getPriorityToPreventFromEviction(const HistoricalInfo historicalInfo) { + // TODO: Improve this logic. + // More recently input entries get higher priority. + return historicalInfo.getTimestamp(); + } + +private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicLanguageModelProbabilityUtils); + + static_assert(MAX_PREV_WORD_COUNT_FOR_N_GRAM <= 2, "Max supported Ngram is Trigram."); + + static const int ASSUMED_MIN_COUNT_FOR_UNIGRAMS; + static const int ASSUMED_MIN_COUNT_FOR_BIGRAMS; + static const int ASSUMED_MIN_COUNT_FOR_TRIGRAMS; + + static const int ENCODED_BACKOFF_WEIGHT_FOR_UNIGRAMS; + static const int ENCODED_BACKOFF_WEIGHT_FOR_BIGRAMS; + static const int ENCODED_BACKOFF_WEIGHT_FOR_TRIGRAMS; + + static const int DURATION_TO_DISCARD_ENTRY_IN_SECONDS; +}; + +} // namespace latinime +#endif /* LATINIME_DYNAMIC_LANGUAGE_MODEL_PROBABILITY_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp index c4297f5d6..31b1ea696 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp @@ -19,28 +19,28 @@ #include <algorithm> #include <cstring> -#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/dynamic_language_model_probability_utils.h" +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" namespace latinime { -const int LanguageModelDictContent::UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE = 0; -const int LanguageModelDictContent::BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE = 1; -const int LanguageModelDictContent::DUMMY_PROBABILITY_FOR_VALID_WORDS = 1; +const int LanguageModelDictContent::TRIE_MAP_BUFFER_INDEX = 0; +const int LanguageModelDictContent::GLOBAL_COUNTERS_BUFFER_INDEX = 1; bool LanguageModelDictContent::save(FILE *const file) const { - return mTrieMap.save(file); + return mTrieMap.save(file) && mGlobalCounters.save(file); } bool LanguageModelDictContent::runGC( const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, - const LanguageModelDictContent *const originalContent, - int *const outNgramCount) { + const LanguageModelDictContent *const originalContent) { return runGCInner(terminalIdMap, originalContent->mTrieMap.getEntriesInRootLevel(), - 0 /* nextLevelBitmapEntryIndex */, outNgramCount); + 0 /* nextLevelBitmapEntryIndex */); } const WordAttributes LanguageModelDictContent::getWordAttributes(const WordIdArrayView prevWordIds, - const int wordId, const HeaderPolicy *const headerPolicy) const { + const int wordId, const bool mustMatchAllPrevWords, + const HeaderPolicy *const headerPolicy) const { int bitmapEntryIndices[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; bitmapEntryIndices[0] = mTrieMap.getRootBitmapEntryIndex(); int maxPrevWordCount = 0; @@ -54,7 +54,15 @@ const WordAttributes LanguageModelDictContent::getWordAttributes(const WordIdArr bitmapEntryIndices[i + 1] = nextBitmapEntryIndex; } + const ProbabilityEntry unigramProbabilityEntry = getProbabilityEntry(wordId); + if (mHasHistoricalInfo && unigramProbabilityEntry.getHistoricalInfo()->getCount() == 0) { + // The word should be treated as a invalid word. + return WordAttributes(); + } for (int i = maxPrevWordCount; i >= 0; --i) { + if (mustMatchAllPrevWords && prevWordIds.size() > static_cast<size_t>(i)) { + break; + } const TrieMap::Result result = mTrieMap.get(wordId, bitmapEntryIndices[i]); if (!result.mIsValid) { continue; @@ -63,38 +71,41 @@ const WordAttributes LanguageModelDictContent::getWordAttributes(const WordIdArr ProbabilityEntry::decode(result.mValue, mHasHistoricalInfo); int probability = NOT_A_PROBABILITY; if (mHasHistoricalInfo) { - const int rawProbability = ForgettingCurveUtils::decodeProbability( - probabilityEntry.getHistoricalInfo(), headerPolicy); - if (rawProbability == NOT_A_PROBABILITY) { - // The entry should not be treated as a valid entry. - continue; - } + const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); + int contextCount = 0; if (i == 0) { // unigram - probability = rawProbability; + contextCount = mGlobalCounters.getTotalCount(); } else { const ProbabilityEntry prevWordProbabilityEntry = getNgramProbabilityEntry( prevWordIds.skip(1 /* n */).limit(i - 1), prevWordIds[0]); if (!prevWordProbabilityEntry.isValid()) { continue; } - if (prevWordProbabilityEntry.representsBeginningOfSentence()) { - probability = rawProbability; - } else { - const int prevWordRawProbability = ForgettingCurveUtils::decodeProbability( - prevWordProbabilityEntry.getHistoricalInfo(), headerPolicy); - probability = std::min(MAX_PROBABILITY - prevWordRawProbability - + rawProbability, MAX_PROBABILITY); + if (prevWordProbabilityEntry.representsBeginningOfSentence() + && historicalInfo->getCount() == 1) { + // BoS ngram requires multiple contextCount. + continue; } + contextCount = prevWordProbabilityEntry.getHistoricalInfo()->getCount(); } + const float rawProbability = + DynamicLanguageModelProbabilityUtils::computeRawProbabilityFromCounts( + historicalInfo->getCount(), contextCount, i + 1); + const int encodedRawProbability = + ProbabilityUtils::encodeRawProbability(rawProbability); + const int decayedProbability = + DynamicLanguageModelProbabilityUtils::getDecayedProbability( + encodedRawProbability, *historicalInfo); + probability = DynamicLanguageModelProbabilityUtils::backoff( + decayedProbability, i + 1 /* n */); } else { probability = probabilityEntry.getProbability(); } // TODO: Some flags in unigramProbabilityEntry should be overwritten by flags in // probabilityEntry. - const ProbabilityEntry unigramProbabilityEntry = getProbabilityEntry(wordId); - return WordAttributes(probability, unigramProbabilityEntry.isNotAWord(), - unigramProbabilityEntry.isBlacklisted(), + return WordAttributes(probability, unigramProbabilityEntry.isBlacklisted(), + unigramProbabilityEntry.isNotAWord(), unigramProbabilityEntry.isPossiblyOffensive()); } // Cannot find the word. @@ -143,28 +154,69 @@ LanguageModelDictContent::EntryRange LanguageModelDictContent::getProbabilityEnt return EntryRange(mTrieMap.getEntriesInSpecifiedLevel(bitmapEntryIndex), mHasHistoricalInfo); } -bool LanguageModelDictContent::truncateEntries(const int *const entryCounts, - const int *const maxEntryCounts, const HeaderPolicy *const headerPolicy, - int *const outEntryCounts) { - for (int i = 0; i <= MAX_PREV_WORD_COUNT_FOR_N_GRAM; ++i) { - if (entryCounts[i] <= maxEntryCounts[i]) { - outEntryCounts[i] = entryCounts[i]; +std::vector<LanguageModelDictContent::DumppedFullEntryInfo> + LanguageModelDictContent::exportAllNgramEntriesRelatedToWord( + const HeaderPolicy *const headerPolicy, const int wordId) const { + const TrieMap::Result result = mTrieMap.getRoot(wordId); + if (!result.mIsValid || result.mNextLevelBitmapEntryIndex == TrieMap::INVALID_INDEX) { + // The word doesn't have any related ngram entries. + return std::vector<DumppedFullEntryInfo>(); + } + std::vector<int> prevWordIds = { wordId }; + std::vector<DumppedFullEntryInfo> entries; + exportAllNgramEntriesRelatedToWordInner(headerPolicy, result.mNextLevelBitmapEntryIndex, + &prevWordIds, &entries); + return entries; +} + +void LanguageModelDictContent::exportAllNgramEntriesRelatedToWordInner( + const HeaderPolicy *const headerPolicy, const int bitmapEntryIndex, + std::vector<int> *const prevWordIds, + std::vector<DumppedFullEntryInfo> *const outBummpedFullEntryInfo) const { + for (const auto &entry : mTrieMap.getEntriesInSpecifiedLevel(bitmapEntryIndex)) { + const int wordId = entry.key(); + const ProbabilityEntry probabilityEntry = + ProbabilityEntry::decode(entry.value(), mHasHistoricalInfo); + if (probabilityEntry.isValid()) { + const WordAttributes wordAttributes = getWordAttributes( + WordIdArrayView(*prevWordIds), wordId, true /* mustMatchAllPrevWords */, + headerPolicy); + outBummpedFullEntryInfo->emplace_back(*prevWordIds, wordId, + wordAttributes, probabilityEntry); + } + if (entry.hasNextLevelMap()) { + prevWordIds->push_back(wordId); + exportAllNgramEntriesRelatedToWordInner(headerPolicy, + entry.getNextLevelBitmapEntryIndex(), prevWordIds, outBummpedFullEntryInfo); + prevWordIds->pop_back(); + } + } +} + +bool LanguageModelDictContent::truncateEntries(const EntryCounts ¤tEntryCounts, + const EntryCounts &maxEntryCounts, const HeaderPolicy *const headerPolicy, + MutableEntryCounters *const outEntryCounters) { + for (int prevWordCount = 0; prevWordCount <= MAX_PREV_WORD_COUNT_FOR_N_GRAM; ++prevWordCount) { + const int totalWordCount = prevWordCount + 1; + if (currentEntryCounts.getNgramCount(totalWordCount) + <= maxEntryCounts.getNgramCount(totalWordCount)) { + outEntryCounters->setNgramCount(totalWordCount, + currentEntryCounts.getNgramCount(totalWordCount)); continue; } - if (!turncateEntriesInSpecifiedLevel(headerPolicy, maxEntryCounts[i], i, - &outEntryCounts[i])) { + int entryCount = 0; + if (!turncateEntriesInSpecifiedLevel(headerPolicy, + maxEntryCounts.getNgramCount(totalWordCount), prevWordCount, &entryCount)) { return false; } + outEntryCounters->setNgramCount(totalWordCount, entryCount); } return true; } bool LanguageModelDictContent::updateAllEntriesOnInputWord(const WordIdArrayView prevWordIds, const int wordId, const bool isValid, const HistoricalInfo historicalInfo, - const HeaderPolicy *const headerPolicy, int *const outAddedNewNgramEntryCount) { - if (outAddedNewNgramEntryCount) { - *outAddedNewNgramEntryCount = 0; - } + const HeaderPolicy *const headerPolicy, MutableEntryCounters *const entryCountersToUpdate) { if (!mHasHistoricalInfo) { AKLOGE("updateAllEntriesOnInputWord is called for dictionary without historical info."); return false; @@ -175,6 +227,9 @@ bool LanguageModelDictContent::updateAllEntriesOnInputWord(const WordIdArrayView if (!setProbabilityEntry(wordId, &updatedUnigramProbabilityEntry)) { return false; } + mGlobalCounters.incrementTotalCount(); + mGlobalCounters.updateMaxValueOfCounters( + updatedUnigramProbabilityEntry.getHistoricalInfo()->getCount()); for (size_t i = 0; i < prevWordIds.size(); ++i) { if (prevWordIds[i] == NOT_A_WORD_ID) { break; @@ -188,8 +243,10 @@ bool LanguageModelDictContent::updateAllEntriesOnInputWord(const WordIdArrayView if (!setNgramProbabilityEntry(limitedPrevWordIds, wordId, &updatedNgramProbabilityEntry)) { return false; } - if (!originalNgramProbabilityEntry.isValid() && outAddedNewNgramEntryCount) { - *outAddedNewNgramEntryCount += 1; + mGlobalCounters.updateMaxValueOfCounters( + updatedNgramProbabilityEntry.getHistoricalInfo()->getCount()); + if (!originalNgramProbabilityEntry.isValid()) { + entryCountersToUpdate->incrementNgramCount(i + 2); } } return true; @@ -198,10 +255,9 @@ bool LanguageModelDictContent::updateAllEntriesOnInputWord(const WordIdArrayView const ProbabilityEntry LanguageModelDictContent::createUpdatedEntryFrom( const ProbabilityEntry &originalProbabilityEntry, const bool isValid, const HistoricalInfo historicalInfo, const HeaderPolicy *const headerPolicy) const { - const HistoricalInfo updatedHistoricalInfo = ForgettingCurveUtils::createUpdatedHistoricalInfo( - originalProbabilityEntry.getHistoricalInfo(), isValid ? - DUMMY_PROBABILITY_FOR_VALID_WORDS : NOT_A_PROBABILITY, - &historicalInfo, headerPolicy); + const HistoricalInfo updatedHistoricalInfo = HistoricalInfo(historicalInfo.getTimestamp(), + 0 /* level */, originalProbabilityEntry.getHistoricalInfo()->getCount() + + historicalInfo.getCount()); if (originalProbabilityEntry.isValid()) { return ProbabilityEntry(originalProbabilityEntry.getFlags(), &updatedHistoricalInfo); } else { @@ -211,8 +267,7 @@ const ProbabilityEntry LanguageModelDictContent::createUpdatedEntryFrom( bool LanguageModelDictContent::runGCInner( const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, - const TrieMap::TrieMapRange trieMapRange, - const int nextLevelBitmapEntryIndex, int *const outNgramCount) { + const TrieMap::TrieMapRange trieMapRange, const int nextLevelBitmapEntryIndex) { for (auto &entry : trieMapRange) { const auto it = terminalIdMap->find(entry.key()); if (it == terminalIdMap->end() || it->second == Ver4DictConstants::NOT_A_TERMINAL_ID) { @@ -222,13 +277,9 @@ bool LanguageModelDictContent::runGCInner( if (!mTrieMap.put(it->second, entry.value(), nextLevelBitmapEntryIndex)) { return false; } - if (outNgramCount) { - *outNgramCount += 1; - } if (entry.hasNextLevelMap()) { if (!runGCInner(terminalIdMap, entry.getEntriesInNextLevel(), - mTrieMap.getNextLevelBitmapEntryIndex(it->second, nextLevelBitmapEntryIndex), - outNgramCount)) { + mTrieMap.getNextLevelBitmapEntryIndex(it->second, nextLevelBitmapEntryIndex))) { return false; } } @@ -237,24 +288,25 @@ bool LanguageModelDictContent::runGCInner( } int LanguageModelDictContent::createAndGetBitmapEntryIndex(const WordIdArrayView prevWordIds) { - if (prevWordIds.empty()) { - return mTrieMap.getRootBitmapEntryIndex(); - } - const int lastBitmapEntryIndex = - getBitmapEntryIndex(prevWordIds.limit(prevWordIds.size() - 1)); - if (lastBitmapEntryIndex == TrieMap::INVALID_INDEX) { - return TrieMap::INVALID_INDEX; - } - const int oldestPrevWordId = prevWordIds.lastOrDefault(NOT_A_WORD_ID); - const TrieMap::Result result = mTrieMap.get(oldestPrevWordId, lastBitmapEntryIndex); - if (!result.mIsValid) { - if (!mTrieMap.put(oldestPrevWordId, - ProbabilityEntry().encode(mHasHistoricalInfo), lastBitmapEntryIndex)) { - return TrieMap::INVALID_INDEX; + int lastBitmapEntryIndex = mTrieMap.getRootBitmapEntryIndex(); + for (const int wordId : prevWordIds) { + const TrieMap::Result result = mTrieMap.get(wordId, lastBitmapEntryIndex); + if (result.mIsValid && result.mNextLevelBitmapEntryIndex != TrieMap::INVALID_INDEX) { + lastBitmapEntryIndex = result.mNextLevelBitmapEntryIndex; + continue; } + if (!result.mIsValid) { + if (!mTrieMap.put(wordId, ProbabilityEntry().encode(mHasHistoricalInfo), + lastBitmapEntryIndex)) { + AKLOGE("Failed to update trie map. wordId: %d, lastBitmapEntryIndex %d", wordId, + lastBitmapEntryIndex); + return TrieMap::INVALID_INDEX; + } + } + lastBitmapEntryIndex = mTrieMap.getNextLevelBitmapEntryIndex(wordId, + lastBitmapEntryIndex); } - return mTrieMap.getNextLevelBitmapEntryIndex(prevWordIds.lastOrDefault(NOT_A_WORD_ID), - lastBitmapEntryIndex); + return lastBitmapEntryIndex; } int LanguageModelDictContent::getBitmapEntryIndex(const WordIdArrayView prevWordIds) const { @@ -271,7 +323,7 @@ int LanguageModelDictContent::getBitmapEntryIndex(const WordIdArrayView prevWord bool LanguageModelDictContent::updateAllProbabilityEntriesForGCInner(const int bitmapEntryIndex, const int prevWordCount, const HeaderPolicy *const headerPolicy, - int *const outEntryCounts) { + const bool needsToHalveCounters, MutableEntryCounters *const outEntryCounters) { for (const auto &entry : mTrieMap.getEntriesInSpecifiedLevel(bitmapEntryIndex)) { if (prevWordCount > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { AKLOGE("Invalid prevWordCount. prevWordCount: %d, MAX_PREV_WORD_COUNT_FOR_N_GRAM: %d.", @@ -288,33 +340,41 @@ bool LanguageModelDictContent::updateAllProbabilityEntriesForGCInner(const int b } continue; } - if (mHasHistoricalInfo && !probabilityEntry.representsBeginningOfSentence() - && probabilityEntry.isValid()) { - const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave( - probabilityEntry.getHistoricalInfo(), headerPolicy); - if (ForgettingCurveUtils::needsToKeep(&historicalInfo, headerPolicy)) { - // Update the entry. - const ProbabilityEntry updatedEntry(probabilityEntry.getFlags(), &historicalInfo); - if (!mTrieMap.put(entry.key(), updatedEntry.encode(mHasHistoricalInfo), - bitmapEntryIndex)) { - return false; - } - } else { + if (mHasHistoricalInfo && probabilityEntry.isValid()) { + const HistoricalInfo *originalHistoricalInfo = probabilityEntry.getHistoricalInfo(); + if (DynamicLanguageModelProbabilityUtils::shouldRemoveEntryDuringGC( + *originalHistoricalInfo)) { // Remove the entry. if (!mTrieMap.remove(entry.key(), bitmapEntryIndex)) { return false; } continue; } + if (needsToHalveCounters) { + const int updatedCount = originalHistoricalInfo->getCount() / 2; + if (updatedCount == 0) { + // Remove the entry. + if (!mTrieMap.remove(entry.key(), bitmapEntryIndex)) { + return false; + } + continue; + } + const HistoricalInfo historicalInfoToSave(originalHistoricalInfo->getTimestamp(), + originalHistoricalInfo->getLevel(), updatedCount); + const ProbabilityEntry updatedEntry(probabilityEntry.getFlags(), + &historicalInfoToSave); + if (!mTrieMap.put(entry.key(), updatedEntry.encode(mHasHistoricalInfo), + bitmapEntryIndex)) { + return false; + } + } } - if (!probabilityEntry.representsBeginningOfSentence()) { - outEntryCounts[prevWordCount] += 1; - } + outEntryCounters->incrementNgramCount(prevWordCount + 1); if (!entry.hasNextLevelMap()) { continue; } if (!updateAllProbabilityEntriesForGCInner(entry.getNextLevelBitmapEntryIndex(), - prevWordCount + 1, headerPolicy, outEntryCounts)) { + prevWordCount + 1, headerPolicy, needsToHalveCounters, outEntryCounters)) { return false; } } @@ -368,11 +428,11 @@ bool LanguageModelDictContent::getEntryInfo(const HeaderPolicy *const headerPoli } const ProbabilityEntry probabilityEntry = ProbabilityEntry::decode(entry.value(), mHasHistoricalInfo); - const int probability = (mHasHistoricalInfo) ? - ForgettingCurveUtils::decodeProbability(probabilityEntry.getHistoricalInfo(), - headerPolicy) : probabilityEntry.getProbability(); - outEntryInfo->emplace_back(probability, - probabilityEntry.getHistoricalInfo()->getTimestamp(), + const int priority = mHasHistoricalInfo + ? DynamicLanguageModelProbabilityUtils::getPriorityToPreventFromEviction( + *probabilityEntry.getHistoricalInfo()) + : probabilityEntry.getProbability(); + outEntryInfo->emplace_back(priority, probabilityEntry.getHistoricalInfo()->getCount(), entry.key(), targetLevel, prevWordIds->data()); } return true; @@ -380,11 +440,11 @@ bool LanguageModelDictContent::getEntryInfo(const HeaderPolicy *const headerPoli bool LanguageModelDictContent::EntryInfoToTurncate::Comparator::operator()( const EntryInfoToTurncate &left, const EntryInfoToTurncate &right) const { - if (left.mProbability != right.mProbability) { - return left.mProbability < right.mProbability; + if (left.mPriority != right.mPriority) { + return left.mPriority < right.mPriority; } - if (left.mTimestamp != right.mTimestamp) { - return left.mTimestamp > right.mTimestamp; + if (left.mCount != right.mCount) { + return left.mCount < right.mCount; } if (left.mKey != right.mKey) { return left.mKey < right.mKey; @@ -401,10 +461,9 @@ bool LanguageModelDictContent::EntryInfoToTurncate::Comparator::operator()( return false; } -LanguageModelDictContent::EntryInfoToTurncate::EntryInfoToTurncate(const int probability, - const int timestamp, const int key, const int prevWordCount, const int *const prevWordIds) - : mProbability(probability), mTimestamp(timestamp), mKey(key), - mPrevWordCount(prevWordCount) { +LanguageModelDictContent::EntryInfoToTurncate::EntryInfoToTurncate(const int priority, + const int count, const int key, const int prevWordCount, const int *const prevWordIds) + : mPriority(priority), mCount(count), mKey(key), mPrevWordCount(prevWordCount) { memmove(mPrevWordIds, prevWordIds, mPrevWordCount * sizeof(mPrevWordIds[0])); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h index 51ef090e1..9678c35f9 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h @@ -22,9 +22,11 @@ #include "defines.h" #include "suggest/core/dictionary/word_attributes.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h" #include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" #include "suggest/policyimpl/dictionary/utils/trie_map.h" #include "utils/byte_array_view.h" #include "utils/int_array_view.h" @@ -40,9 +42,6 @@ class HeaderPolicy; */ class LanguageModelDictContent { public: - static const int UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE; - static const int BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE; - // Pair of word id and probability entry used for iteration. class WordIdAndProbabilityEntry { public: @@ -112,31 +111,54 @@ class LanguageModelDictContent { const bool mHasHistoricalInfo; }; - LanguageModelDictContent(const ReadWriteByteArrayView trieMapBuffer, + class DumppedFullEntryInfo { + public: + DumppedFullEntryInfo(std::vector<int> &prevWordIds, const int targetWordId, + const WordAttributes &wordAttributes, const ProbabilityEntry &probabilityEntry) + : mPrevWordIds(prevWordIds), mTargetWordId(targetWordId), + mWordAttributes(wordAttributes), mProbabilityEntry(probabilityEntry) {} + + const WordIdArrayView getPrevWordIds() const { return WordIdArrayView(mPrevWordIds); } + int getTargetWordId() const { return mTargetWordId; } + const WordAttributes &getWordAttributes() const { return mWordAttributes; } + const ProbabilityEntry &getProbabilityEntry() const { return mProbabilityEntry; } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(DumppedFullEntryInfo); + + const std::vector<int> mPrevWordIds; + const int mTargetWordId; + const WordAttributes mWordAttributes; + const ProbabilityEntry mProbabilityEntry; + }; + + LanguageModelDictContent(const ReadWriteByteArrayView *const buffers, const bool hasHistoricalInfo) - : mTrieMap(trieMapBuffer), mHasHistoricalInfo(hasHistoricalInfo) {} + : mTrieMap(buffers[TRIE_MAP_BUFFER_INDEX]), + mGlobalCounters(buffers[GLOBAL_COUNTERS_BUFFER_INDEX]), + mHasHistoricalInfo(hasHistoricalInfo) {} explicit LanguageModelDictContent(const bool hasHistoricalInfo) - : mTrieMap(), mHasHistoricalInfo(hasHistoricalInfo) {} + : mTrieMap(), mGlobalCounters(), mHasHistoricalInfo(hasHistoricalInfo) {} bool isNearSizeLimit() const { - return mTrieMap.isNearSizeLimit(); + return mTrieMap.isNearSizeLimit() || mGlobalCounters.needsToHalveCounters(); } bool save(FILE *const file) const; bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, - const LanguageModelDictContent *const originalContent, - int *const outNgramCount); + const LanguageModelDictContent *const originalContent); const WordAttributes getWordAttributes(const WordIdArrayView prevWordIds, const int wordId, - const HeaderPolicy *const headerPolicy) const; + const bool mustMatchAllPrevWords, const HeaderPolicy *const headerPolicy) const; ProbabilityEntry getProbabilityEntry(const int wordId) const { return getNgramProbabilityEntry(WordIdArrayView(), wordId); } bool setProbabilityEntry(const int wordId, const ProbabilityEntry *const probabilityEntry) { + mGlobalCounters.addToTotalCount(probabilityEntry->getHistoricalInfo()->getCount()); return setNgramProbabilityEntry(WordIdArrayView(), wordId, probabilityEntry); } @@ -154,22 +176,30 @@ class LanguageModelDictContent { EntryRange getProbabilityEntries(const WordIdArrayView prevWordIds) const; + std::vector<DumppedFullEntryInfo> exportAllNgramEntriesRelatedToWord( + const HeaderPolicy *const headerPolicy, const int wordId) const; + bool updateAllProbabilityEntriesForGC(const HeaderPolicy *const headerPolicy, - int *const outEntryCounts) { - for (int i = 0; i <= MAX_PREV_WORD_COUNT_FOR_N_GRAM; ++i) { - outEntryCounts[i] = 0; + MutableEntryCounters *const outEntryCounters) { + if (!updateAllProbabilityEntriesForGCInner(mTrieMap.getRootBitmapEntryIndex(), + 0 /* prevWordCount */, headerPolicy, mGlobalCounters.needsToHalveCounters(), + outEntryCounters)) { + return false; + } + if (mGlobalCounters.needsToHalveCounters()) { + mGlobalCounters.halveCounters(); } - return updateAllProbabilityEntriesForGCInner(mTrieMap.getRootBitmapEntryIndex(), - 0 /* prevWordCount */, headerPolicy, outEntryCounts); + return true; } // entryCounts should be created by updateAllProbabilityEntries. - bool truncateEntries(const int *const entryCounts, const int *const maxEntryCounts, - const HeaderPolicy *const headerPolicy, int *const outEntryCounts); + bool truncateEntries(const EntryCounts ¤tEntryCounts, const EntryCounts &maxEntryCounts, + const HeaderPolicy *const headerPolicy, MutableEntryCounters *const outEntryCounters); bool updateAllEntriesOnInputWord(const WordIdArrayView prevWordIds, const int wordId, const bool isValid, const HistoricalInfo historicalInfo, - const HeaderPolicy *const headerPolicy, int *const outAddedNewNgramEntryCount); + const HeaderPolicy *const headerPolicy, + MutableEntryCounters *const entryCountersToUpdate); private: DISALLOW_COPY_AND_ASSIGN(LanguageModelDictContent); @@ -184,11 +214,12 @@ class LanguageModelDictContent { DISALLOW_ASSIGNMENT_OPERATOR(Comparator); }; - EntryInfoToTurncate(const int probability, const int timestamp, const int key, + EntryInfoToTurncate(const int priority, const int count, const int key, const int prevWordCount, const int *const prevWordIds); - int mProbability; - int mTimestamp; + int mPriority; + // TODO: Remove. + int mCount; int mKey; int mPrevWordCount; int mPrevWordIds[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; @@ -197,19 +228,20 @@ class LanguageModelDictContent { DISALLOW_DEFAULT_CONSTRUCTOR(EntryInfoToTurncate); }; - // TODO: Remove - static const int DUMMY_PROBABILITY_FOR_VALID_WORDS; + static const int TRIE_MAP_BUFFER_INDEX; + static const int GLOBAL_COUNTERS_BUFFER_INDEX; TrieMap mTrieMap; + LanguageModelDictContentGlobalCounters mGlobalCounters; const bool mHasHistoricalInfo; bool runGCInner(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, - const TrieMap::TrieMapRange trieMapRange, const int nextLevelBitmapEntryIndex, - int *const outNgramCount); + const TrieMap::TrieMapRange trieMapRange, const int nextLevelBitmapEntryIndex); int createAndGetBitmapEntryIndex(const WordIdArrayView prevWordIds); int getBitmapEntryIndex(const WordIdArrayView prevWordIds) const; bool updateAllProbabilityEntriesForGCInner(const int bitmapEntryIndex, const int prevWordCount, - const HeaderPolicy *const headerPolicy, int *const outEntryCounts); + const HeaderPolicy *const headerPolicy, const bool needsToHalveCounters, + MutableEntryCounters *const outEntryCounters); bool turncateEntriesInSpecifiedLevel(const HeaderPolicy *const headerPolicy, const int maxEntryCount, const int targetLevel, int *const outEntryCount); bool getEntryInfo(const HeaderPolicy *const headerPolicy, const int targetLevel, @@ -218,6 +250,9 @@ class LanguageModelDictContent { const ProbabilityEntry createUpdatedEntryFrom(const ProbabilityEntry &originalProbabilityEntry, const bool isValid, const HistoricalInfo historicalInfo, const HeaderPolicy *const headerPolicy) const; + void exportAllNgramEntriesRelatedToWordInner(const HeaderPolicy *const headerPolicy, + const int bitmapEntryIndex, std::vector<int> *const prevWordIds, + std::vector<DumppedFullEntryInfo> *const outBummpedFullEntryInfo) const; }; } // namespace latinime #endif /* LATINIME_LANGUAGE_MODEL_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.cpp new file mode 100644 index 000000000..d6d91887e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.cpp @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h" + +#include <climits> + +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" + +namespace latinime { + +const int LanguageModelDictContentGlobalCounters::COUNTER_VALUE_NEAR_LIMIT_THRESHOLD = + (1 << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT)) - 64; +const int LanguageModelDictContentGlobalCounters::TOTAL_COUNT_VALUE_NEAR_LIMIT_THRESHOLD = 1 << 30; +const int LanguageModelDictContentGlobalCounters::COUNTER_SIZE_IN_BYTES = 4; +const int LanguageModelDictContentGlobalCounters::TOTAL_COUNT_INDEX = 0; +const int LanguageModelDictContentGlobalCounters::MAX_VALUE_OF_COUNTERS_INDEX = 1; + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h new file mode 100644 index 000000000..283c2691a --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef LATINIME_LANGUAGE_MODEL_DICT_CONTENT_GLOBAL_COUNTERS_H +#define LATINIME_LANGUAGE_MODEL_DICT_CONTENT_GLOBAL_COUNTERS_H + +#include <cstdio> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "utils/byte_array_view.h" + +namespace latinime { + +class LanguageModelDictContentGlobalCounters { + public: + explicit LanguageModelDictContentGlobalCounters(const ReadWriteByteArrayView buffer) + : mBuffer(buffer, 0 /* maxAdditionalBufferSize */), + mTotalCount(readValue(mBuffer, TOTAL_COUNT_INDEX)), + mMaxValueOfCounters(readValue(mBuffer, MAX_VALUE_OF_COUNTERS_INDEX)) {} + + LanguageModelDictContentGlobalCounters() + : mBuffer(0 /* maxAdditionalBufferSize */), mTotalCount(0), mMaxValueOfCounters(0) {} + + bool needsToHalveCounters() const { + return mMaxValueOfCounters >= COUNTER_VALUE_NEAR_LIMIT_THRESHOLD + || mTotalCount >= TOTAL_COUNT_VALUE_NEAR_LIMIT_THRESHOLD; + } + + int getTotalCount() const { + return mTotalCount; + } + + bool save(FILE *const file) const { + BufferWithExtendableBuffer bufferToWrite( + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); + if (!bufferToWrite.writeUint(mTotalCount, COUNTER_SIZE_IN_BYTES, + TOTAL_COUNT_INDEX * COUNTER_SIZE_IN_BYTES)) { + return false; + } + if (!bufferToWrite.writeUint(mMaxValueOfCounters, COUNTER_SIZE_IN_BYTES, + MAX_VALUE_OF_COUNTERS_INDEX * COUNTER_SIZE_IN_BYTES)) { + return false; + } + return DictFileWritingUtils::writeBufferToFileTail(file, &bufferToWrite); + } + + void incrementTotalCount() { + mTotalCount += 1; + } + + void addToTotalCount(const int count) { + mTotalCount += count; + } + + void updateMaxValueOfCounters(const int count) { + mMaxValueOfCounters = std::max(count, mMaxValueOfCounters); + } + + void halveCounters() { + mMaxValueOfCounters /= 2; + mTotalCount /= 2; + } + +private: + DISALLOW_COPY_AND_ASSIGN(LanguageModelDictContentGlobalCounters); + + const static int COUNTER_VALUE_NEAR_LIMIT_THRESHOLD; + const static int TOTAL_COUNT_VALUE_NEAR_LIMIT_THRESHOLD; + const static int COUNTER_SIZE_IN_BYTES; + const static int TOTAL_COUNT_INDEX; + const static int MAX_VALUE_OF_COUNTERS_INDEX; + + BufferWithExtendableBuffer mBuffer; + int mTotalCount; + int mMaxValueOfCounters; + + static int readValue(const BufferWithExtendableBuffer &buffer, const int index) { + const int pos = COUNTER_SIZE_IN_BYTES * index; + if (pos + COUNTER_SIZE_IN_BYTES > buffer.getTailPosition()) { + return 0; + } + return buffer.readUint(COUNTER_SIZE_IN_BYTES, pos); + } +}; +} // namespace latinime +#endif /* LATINIME_LANGUAGE_MODEL_DICT_CONTENT_GLOBAL_COUNTERS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h index f4d340f86..9c4ab18e4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h @@ -105,7 +105,7 @@ class ProbabilityEntry { encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_LEVEL_FIELD_SIZE * CHAR_BIT)) | static_cast<uint8_t>(mHistoricalInfo.getLevel()); encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT)) - | static_cast<uint8_t>(mHistoricalInfo.getCount()); + | static_cast<uint16_t>(mHistoricalInfo.getCount()); } else { encodedEntry = (encodedEntry << (Ver4DictConstants::PROBABILITY_SIZE * CHAR_BIT)) | static_cast<uint8_t>(mProbability); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp index 45f88e9b2..4d088dcab 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp @@ -179,7 +179,7 @@ Ver4DictBuffers::Ver4DictBuffers(MmappedBuffer::MmappedBufferPtr &&headerBuffer, BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mTerminalPositionLookupTable( contentBuffers[Ver4DictConstants::TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX]), - mLanguageModelDictContent(contentBuffers[Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX], + mLanguageModelDictContent(&contentBuffers[Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX], mHeaderPolicy.hasHistoricalInfoOfWords()), mShortcutDictContent(&contentBuffers[Ver4DictConstants::SHORTCUT_BUFFERS_INDEX]), mIsUpdatable(mDictBuffer->isUpdatable()) {} diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp index 8e6cb974b..bd89b8da7 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp @@ -49,8 +49,8 @@ const int Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3; const int Ver4DictConstants::NOT_A_TERMINAL_ADDRESS = 0; const int Ver4DictConstants::TERMINAL_ID_FIELD_SIZE = 4; const int Ver4DictConstants::TIME_STAMP_FIELD_SIZE = 4; -const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 1; -const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 1; +const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 0; +const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 2; const uint8_t Ver4DictConstants::FLAG_REPRESENTS_BEGINNING_OF_SENTENCE = 0x1; const uint8_t Ver4DictConstants::FLAG_NOT_A_VALID_ENTRY = 0x2; @@ -67,6 +67,6 @@ const int Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK = 0x80; const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT = 1; const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT = 3; -const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT = 1; +const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT = 2; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h index 600b5ffe4..13d7a5714 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h @@ -47,6 +47,7 @@ class Ver4DictConstants { static const int NOT_A_TERMINAL_ADDRESS; static const int TERMINAL_ID_FIELD_SIZE; static const int TIME_STAMP_FIELD_SIZE; + // TODO: Remove static const int WORD_LEVEL_FIELD_SIZE; static const int WORD_COUNT_FIELD_SIZE; // Flags in probability entry. diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp index 794c63ffd..3488f7d2a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp @@ -342,7 +342,7 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeFlags(const int ptNodePos, const bo // Create node flags and write them. PatriciaTrieReadingUtils::NodeFlags nodeFlags = PatriciaTrieReadingUtils::createAndGetFlags(false /* isNotAWord */, - false /* isBlacklisted */, isTerminal, false /* hasShortcutTargets */, + false /* isPossiblyOffensive */, isTerminal, false /* hasShortcutTargets */, false /* hasBigrams */, hasMultipleChars, CHILDREN_POSITION_FIELD_SIZE); if (!DynamicPtWritingUtils::writeFlags(mTrieBuffer, nodeFlags, ptNodePos)) { AKLOGE("Cannot write PtNode flags. flags: %x, pos: %d", nodeFlags, ptNodePos); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp index ea8c0dc22..1992d4a5a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp @@ -97,6 +97,9 @@ int Ver4PatriciaTriePolicy::getWordId(const CodePointArrayView wordCodePoints, return NOT_A_WORD_ID; } const PtNodeParams ptNodeParams = mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos); + if (ptNodeParams.isDeleted()) { + return NOT_A_WORD_ID; + } return ptNodeParams.getTerminalId(); } @@ -107,7 +110,7 @@ const WordAttributes Ver4PatriciaTriePolicy::getWordAttributesInContext( return WordAttributes(); } return mBuffers->getLanguageModelDictContent()->getWordAttributes(prevWordIds, wordId, - mHeaderPolicy); + false /* mustMatchAllPrevWords */, mHeaderPolicy); } int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordIds, @@ -115,18 +118,13 @@ int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordI if (wordId == NOT_A_WORD_ID || prevWordIds.contains(NOT_A_WORD_ID)) { return NOT_A_PROBABILITY; } - const ProbabilityEntry probabilityEntry = - mBuffers->getLanguageModelDictContent()->getNgramProbabilityEntry(prevWordIds, wordId); - if (!probabilityEntry.isValid() || probabilityEntry.isBlacklisted() - || probabilityEntry.isNotAWord()) { + const WordAttributes wordAttributes = + mBuffers->getLanguageModelDictContent()->getWordAttributes(prevWordIds, wordId, + true /* mustMatchAllPrevWords */, mHeaderPolicy); + if (wordAttributes.isBlacklisted() || wordAttributes.isNotAWord()) { return NOT_A_PROBABILITY; } - if (mHeaderPolicy->hasHistoricalInfoOfWords()) { - return ForgettingCurveUtils::decodeProbability(probabilityEntry.getHistoricalInfo(), - mHeaderPolicy); - } else { - return probabilityEntry.getProbability(); - } + return wordAttributes.getProbability(); } BinaryDictionaryShortcutIterator Ver4PatriciaTriePolicy::getShortcutIterator( @@ -148,10 +146,16 @@ void Ver4PatriciaTriePolicy::iterateNgramEntries(const WordIdArrayView prevWordI if (!probabilityEntry.isValid()) { continue; } - const int probability = probabilityEntry.hasHistoricalInfo() ? - ForgettingCurveUtils::decodeProbability( - probabilityEntry.getHistoricalInfo(), mHeaderPolicy) : - probabilityEntry.getProbability(); + int probability = NOT_A_PROBABILITY; + if (probabilityEntry.hasHistoricalInfo()) { + // TODO: Quit checking count here. + // If count <= 1, the word can be an invaild word. The actual probability should + // be checked using getWordAttributesInContext() in onVisitEntry(). + probability = probabilityEntry.getHistoricalInfo()->getCount() <= 1 ? + NOT_A_PROBABILITY : 0; + } else { + probability = probabilityEntry.getProbability(); + } listener->onVisitEntry(probability, entry.getWordId()); } } @@ -211,7 +215,7 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const CodePointArrayView wordCodePo if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointArrayView, unigramProperty, &addedNewUnigram)) { if (addedNewUnigram && !unigramProperty->representsBeginningOfSentence()) { - mUnigramCount++; + mEntryCounters.incrementUnigramCount(); } if (unigramProperty->getShortcuts().size() > 0) { // Add shortcut target. @@ -259,13 +263,12 @@ bool Ver4PatriciaTriePolicy::removeUnigramEntry(const CodePointArrayView wordCod return false; } if (!ptNodeParams.representsNonWordInfo()) { - mUnigramCount--; + mEntryCounters.decrementUnigramCount(); } return true; } -bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty) { +bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramProperty *const ngramProperty) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); return false; @@ -275,6 +278,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContex mDictBuffer->getTailPosition()); return false; } + const NgramContext *const ngramContext = ngramProperty->getNgramContext(); if (!ngramContext->isValid()) { AKLOGE("Ngram context is not valid for adding n-gram entry to the dictionary."); return false; @@ -299,7 +303,8 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContex } const UnigramProperty beginningOfSentenceUnigramProperty( true /* representsBeginningOfSentence */, true /* isNotAWord */, - false /* isBlacklisted */, MAX_PROBABILITY /* probability */, HistoricalInfo()); + false /* isBlacklisted */, false /* isPossiblyOffensive */, + MAX_PROBABILITY /* probability */, HistoricalInfo()); if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */), &beginningOfSentenceUnigramProperty)) { AKLOGE("Cannot add unigram entry for the beginning-of-sentence."); @@ -316,7 +321,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContex bool addedNewEntry = false; if (mNodeWriter.addNgramEntry(prevWordIds, wordId, ngramProperty, &addedNewEntry)) { if (addedNewEntry) { - mBigramCount++; + mEntryCounters.incrementNgramCount(prevWordIds.size() + 1); } return true; } else { @@ -354,7 +359,7 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const NgramContext *const ngramCon return false; } if (mNodeWriter.removeNgramEntry(prevWordIds, wordId)) { - mBigramCount--; + mEntryCounters.decrementNgramCount(prevWordIds.size()); return true; } else { return false; @@ -375,38 +380,47 @@ bool Ver4PatriciaTriePolicy::updateEntriesForWordWithNgramContext( if (wordId == NOT_A_WORD_ID) { // The word is not in the dictionary. const UnigramProperty unigramProperty(false /* representsBeginningOfSentence */, - false /* isNotAWord */, false /* isBlacklisted */, NOT_A_PROBABILITY, - HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, 0 /* count */)); + false /* isNotAWord */, false /* isBlacklisted */, false /* isPossiblyOffensive */, + NOT_A_PROBABILITY, HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, + 0 /* count */)); if (!addUnigramEntry(wordCodePoints, &unigramProperty)) { AKLOGE("Cannot add unigarm entry in updateEntriesForWordWithNgramContext()."); return false; } + if (!isValidWord) { + return true; + } wordId = getWordId(wordCodePoints, false /* tryLowerCaseSearch */); } WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); - if (prevWordIds.firstOrDefault(NOT_A_WORD_ID) == NOT_A_WORD_ID - && ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */)) { - const UnigramProperty beginningOfSentenceUnigramProperty( - true /* representsBeginningOfSentence */, - true /* isNotAWord */, false /* isBlacklisted */, NOT_A_PROBABILITY, - HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, 0 /* count */)); - if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */), - &beginningOfSentenceUnigramProperty)) { - AKLOGE("Cannot add BoS entry in updateEntriesForWordWithNgramContext()."); + if (ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */)) { + if (prevWordIds.firstOrDefault(NOT_A_WORD_ID) == NOT_A_WORD_ID) { + const UnigramProperty beginningOfSentenceUnigramProperty( + true /* representsBeginningOfSentence */, + true /* isNotAWord */, false /* isPossiblyOffensive */, NOT_A_PROBABILITY, + HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, 0 /* count */)); + if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */), + &beginningOfSentenceUnigramProperty)) { + AKLOGE("Cannot add BoS entry in updateEntriesForWordWithNgramContext()."); + return false; + } + // Refresh word ids. + ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); + } + // Update entries for beginning of sentence. + if (!mBuffers->getMutableLanguageModelDictContent()->updateAllEntriesOnInputWord( + prevWordIds.skip(1 /* n */), prevWordIds[0], true /* isVaild */, historicalInfo, + mHeaderPolicy, &mEntryCounters)) { return false; } - // Refresh word ids. - ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); } - int addedNewNgramEntryCount = 0; if (!mBuffers->getMutableLanguageModelDictContent()->updateAllEntriesOnInputWord(prevWordIds, - wordId, updateAsAValidWord, historicalInfo, mHeaderPolicy, &addedNewNgramEntryCount)) { + wordId, updateAsAValidWord, historicalInfo, mHeaderPolicy, &mEntryCounters)) { return false; } - mBigramCount += addedNewNgramEntryCount; return true; } @@ -415,7 +429,7 @@ bool Ver4PatriciaTriePolicy::flush(const char *const filePath) { AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath); return false; } - if (!mWritingHelper.writeToDictFile(filePath, mUnigramCount, mBigramCount)) { + if (!mWritingHelper.writeToDictFile(filePath, mEntryCounters.getEntryCounts())) { AKLOGE("Cannot flush the dictionary to file."); mIsCorrupted = true; return false; @@ -453,7 +467,7 @@ bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { // Needs to reduce dictionary size. return true; } else if (mHeaderPolicy->isDecayingDict()) { - return ForgettingCurveUtils::needsToDecay(mindsBlockByGC, mUnigramCount, mBigramCount, + return ForgettingCurveUtils::needsToDecay(mindsBlockByGC, mEntryCounters.getEntryCounts(), mHeaderPolicy); } return false; @@ -463,19 +477,19 @@ void Ver4PatriciaTriePolicy::getProperty(const char *const query, const int quer char *const outResult, const int maxResultLength) { const int compareLength = queryLength + 1 /* terminator */; if (strncmp(query, UNIGRAM_COUNT_QUERY, compareLength) == 0) { - snprintf(outResult, maxResultLength, "%d", mUnigramCount); + snprintf(outResult, maxResultLength, "%d", mEntryCounters.getUnigramCount()); } else if (strncmp(query, BIGRAM_COUNT_QUERY, compareLength) == 0) { - snprintf(outResult, maxResultLength, "%d", mBigramCount); + snprintf(outResult, maxResultLength, "%d", mEntryCounters.getBigramCount()); } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, compareLength) == 0) { snprintf(outResult, maxResultLength, "%d", mHeaderPolicy->isDecayingDict() ? - ForgettingCurveUtils::getUnigramCountHardLimit( + ForgettingCurveUtils::getEntryCountHardLimit( mHeaderPolicy->getMaxUnigramCount()) : static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, compareLength) == 0) { snprintf(outResult, maxResultLength, "%d", mHeaderPolicy->isDecayingDict() ? - ForgettingCurveUtils::getBigramCountHardLimit( + ForgettingCurveUtils::getEntryCountHardLimit( mHeaderPolicy->getMaxBigramCount()) : static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); } @@ -488,29 +502,37 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty( AKLOGE("getWordProperty is called for invalid word."); return WordProperty(); } - const int ptNodePos = - mBuffers->getTerminalPositionLookupTable()->getTerminalPtNodePosition(wordId); - const PtNodeParams ptNodeParams = mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos); - const ProbabilityEntry probabilityEntry = - mBuffers->getLanguageModelDictContent()->getProbabilityEntry( - ptNodeParams.getTerminalId()); - const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); - // Fetch bigram information. - // TODO: Support n-gram. + const LanguageModelDictContent *const languageModelDictContent = + mBuffers->getLanguageModelDictContent(); + // Fetch ngram information. std::vector<NgramProperty> ngrams; - const WordIdArrayView prevWordIds = WordIdArrayView::singleElementView(&wordId); - int bigramWord1CodePoints[MAX_WORD_LENGTH]; - for (const auto entry : mBuffers->getLanguageModelDictContent()->getProbabilityEntries( - prevWordIds)) { - const int codePointCount = getCodePointsAndReturnCodePointCount(entry.getWordId(), - MAX_WORD_LENGTH, bigramWord1CodePoints); - const ProbabilityEntry probabilityEntry = entry.getProbabilityEntry(); - const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); - const int probability = probabilityEntry.hasHistoricalInfo() ? - ForgettingCurveUtils::decodeProbability(historicalInfo, mHeaderPolicy) : - probabilityEntry.getProbability(); - ngrams.emplace_back(CodePointArrayView(bigramWord1CodePoints, codePointCount).toVector(), - probability, *historicalInfo); + int ngramTargetCodePoints[MAX_WORD_LENGTH]; + int ngramPrevWordsCodePoints[MAX_PREV_WORD_COUNT_FOR_N_GRAM][MAX_WORD_LENGTH]; + int ngramPrevWordsCodePointCount[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + bool ngramPrevWordIsBeginningOfSentense[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + for (const auto entry : languageModelDictContent->exportAllNgramEntriesRelatedToWord( + mHeaderPolicy, wordId)) { + const int codePointCount = getCodePointsAndReturnCodePointCount(entry.getTargetWordId(), + MAX_WORD_LENGTH, ngramTargetCodePoints); + const WordIdArrayView prevWordIds = entry.getPrevWordIds(); + for (size_t i = 0; i < prevWordIds.size(); ++i) { + ngramPrevWordsCodePointCount[i] = getCodePointsAndReturnCodePointCount(prevWordIds[i], + MAX_WORD_LENGTH, ngramPrevWordsCodePoints[i]); + ngramPrevWordIsBeginningOfSentense[i] = languageModelDictContent->getProbabilityEntry( + prevWordIds[i]).representsBeginningOfSentence(); + if (ngramPrevWordIsBeginningOfSentense[i]) { + ngramPrevWordsCodePointCount[i] = CharUtils::removeBeginningOfSentenceMarker( + ngramPrevWordsCodePoints[i], ngramPrevWordsCodePointCount[i]); + } + } + const NgramContext ngramContext(ngramPrevWordsCodePoints, ngramPrevWordsCodePointCount, + ngramPrevWordIsBeginningOfSentense, prevWordIds.size()); + const ProbabilityEntry ngramProbabilityEntry = entry.getProbabilityEntry(); + const HistoricalInfo *const historicalInfo = ngramProbabilityEntry.getHistoricalInfo(); + // TODO: Output flags in WordAttributes. + ngrams.emplace_back(ngramContext, + CodePointArrayView(ngramTargetCodePoints, codePointCount).toVector(), + entry.getWordAttributes().getProbability(), *historicalInfo); } // Fetch shortcut information. std::vector<UnigramProperty::ShortcutProperty> shortcuts; @@ -530,9 +552,14 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty( shortcutProbability); } } + const WordAttributes wordAttributes = languageModelDictContent->getWordAttributes( + WordIdArrayView(), wordId, true /* mustMatchAllPrevWords */, mHeaderPolicy); + const ProbabilityEntry probabilityEntry = languageModelDictContent->getProbabilityEntry(wordId); + const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); const UnigramProperty unigramProperty(probabilityEntry.representsBeginningOfSentence(), - probabilityEntry.isNotAWord(), probabilityEntry.isBlacklisted(), - probabilityEntry.getProbability(), *historicalInfo, std::move(shortcuts)); + wordAttributes.isNotAWord(), wordAttributes.isBlacklisted(), + wordAttributes.isPossiblyOffensive(), wordAttributes.getProbability(), + *historicalInfo, std::move(shortcuts)); return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h index c0532815c..13700b390 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h @@ -30,6 +30,7 @@ #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" #include "utils/int_array_view.h" namespace latinime { @@ -37,7 +38,6 @@ namespace latinime { class DicNode; class DicNodeVector; -// TODO: Support counting ngram entries. // Word id = Artificial id that is stored in the PtNode looked up by the word. class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { public: @@ -51,8 +51,8 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { &mShortcutPolicy), mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter), mWritingHelper(mBuffers.get()), - mUnigramCount(mHeaderPolicy->getUnigramCount()), - mBigramCount(mHeaderPolicy->getBigramCount()), + mEntryCounters(mHeaderPolicy->getUnigramCount(), mHeaderPolicy->getBigramCount(), + mHeaderPolicy->getTrigramCount()), mTerminalPtNodePositionsForIteratingWords(), mIsCorrupted(false) {}; AK_FORCE_INLINE int getRootPosition() const { @@ -92,8 +92,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { bool removeUnigramEntry(const CodePointArrayView wordCodePoints); - bool addNgramEntry(const NgramContext *const ngramContext, - const NgramProperty *const ngramProperty); + bool addNgramEntry(const NgramProperty *const ngramProperty); bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints); @@ -141,9 +140,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { Ver4PatriciaTrieNodeWriter mNodeWriter; DynamicPtUpdatingHelper mUpdatingHelper; Ver4PatriciaTrieWritingHelper mWritingHelper; - int mUnigramCount; - // TODO: Support counting ngram entries. - int mBigramCount; + MutableEntryCounters mEntryCounters; std::vector<int> mTerminalPtNodePositionsForIteratingWords; mutable bool mIsCorrupted; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp index f0d59c150..7f0604ce8 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp @@ -33,17 +33,18 @@ namespace latinime { bool Ver4PatriciaTrieWritingHelper::writeToDictFile(const char *const dictDirPath, - const int unigramCount, const int bigramCount) const { + const EntryCounts &entryCounts) const { const HeaderPolicy *const headerPolicy = mBuffers->getHeaderPolicy(); BufferWithExtendableBuffer headerBuffer( BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); const int extendedRegionSize = headerPolicy->getExtendedRegionSize() + mBuffers->getTrieBuffer()->getUsedAdditionalBufferSize(); if (!headerPolicy->fillInAndWriteHeaderToBuffer(false /* updatesLastDecayedTime */, - unigramCount, bigramCount, extendedRegionSize, &headerBuffer)) { + entryCounts, extendedRegionSize, &headerBuffer)) { AKLOGE("Cannot write header structure to buffer. " - "updatesLastDecayedTime: %d, unigramCount: %d, bigramCount: %d, " - "extendedRegionSize: %d", false, unigramCount, bigramCount, + "updatesLastDecayedTime: %d, unigramCount: %d, bigramCount: %d, trigramCount: %d," + "extendedRegionSize: %d", false, entryCounts.getUnigramCount(), + entryCounts.getBigramCount(), entryCounts.getTrigramCount(), extendedRegionSize); return false; } @@ -56,15 +57,14 @@ bool Ver4PatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeAr Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( Ver4DictBuffers::createVer4DictBuffers(headerPolicy, Ver4DictConstants::MAX_DICTIONARY_SIZE)); - int unigramCount = 0; - int bigramCount = 0; - if (!runGC(rootPtNodeArrayPos, headerPolicy, dictBuffers.get(), &unigramCount, &bigramCount)) { + MutableEntryCounters entryCounters; + if (!runGC(rootPtNodeArrayPos, headerPolicy, dictBuffers.get(), &entryCounters)) { return false; } BufferWithExtendableBuffer headerBuffer( BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); if (!headerPolicy->fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */, - unigramCount, bigramCount, 0 /* extendedRegionSize */, &headerBuffer)) { + entryCounters.getEntryCounts(), 0 /* extendedRegionSize */, &headerBuffer)) { return false; } return dictBuffers->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); @@ -72,7 +72,7 @@ bool Ver4PatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeAr bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, const HeaderPolicy *const headerPolicy, Ver4DictBuffers *const buffersToWrite, - int *const outUnigramCount, int *const outBigramCount) { + MutableEntryCounters *const outEntryCounters) { Ver4PatriciaTrieNodeReader ptNodeReader(mBuffers->getTrieBuffer()); Ver4PtNodeArrayReader ptNodeArrayReader(mBuffers->getTrieBuffer()); Ver4ShortcutListPolicy shortcutPolicy(mBuffers->getMutableShortcutDictContent(), @@ -80,24 +80,17 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, Ver4PatriciaTrieNodeWriter ptNodeWriter(mBuffers->getWritableTrieBuffer(), mBuffers, &ptNodeReader, &ptNodeArrayReader, &shortcutPolicy); - int entryCountTable[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; if (!mBuffers->getMutableLanguageModelDictContent()->updateAllProbabilityEntriesForGC( - headerPolicy, entryCountTable)) { + headerPolicy, outEntryCounters)) { AKLOGE("Failed to update probabilities in language model dict content."); return false; } if (headerPolicy->isDecayingDict()) { - int maxEntryCountTable[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; - maxEntryCountTable[LanguageModelDictContent::UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE] = - headerPolicy->getMaxUnigramCount(); - maxEntryCountTable[LanguageModelDictContent::BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE] = - headerPolicy->getMaxBigramCount(); - for (size_t i = 2; i < NELEMS(maxEntryCountTable); ++i) { - // TODO: Have max n-gram count. - maxEntryCountTable[i] = headerPolicy->getMaxBigramCount(); - } - if (!mBuffers->getMutableLanguageModelDictContent()->truncateEntries(entryCountTable, - maxEntryCountTable, headerPolicy, entryCountTable)) { + const EntryCounts maxEntryCounts(headerPolicy->getMaxUnigramCount(), + headerPolicy->getMaxBigramCount(), headerPolicy->getMaxTrigramCount()); + if (!mBuffers->getMutableLanguageModelDictContent()->truncateEntries( + outEntryCounters->getEntryCounts(), maxEntryCounts, headerPolicy, + outEntryCounters)) { AKLOGE("Failed to truncate entries in language model dict content."); return false; } @@ -141,9 +134,9 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, &terminalIdMap)) { return false; } - // Run GC for probability dict content. + // Run GC for language model dict content. if (!buffersToWrite->getMutableLanguageModelDictContent()->runGC(&terminalIdMap, - mBuffers->getLanguageModelDictContent(), nullptr /* outNgramCount */)) { + mBuffers->getLanguageModelDictContent())) { return false; } // Run GC for shortcut dict content. @@ -166,10 +159,6 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, &traversePolicyToUpdateAllPtNodeFlagsAndTerminalIds)) { return false; } - *outUnigramCount = - entryCountTable[LanguageModelDictContent::UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE]; - *outBigramCount = - entryCountTable[LanguageModelDictContent::BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE]; return true; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h index 3569d0576..c56cea5cf 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h @@ -20,6 +20,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.h" #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" namespace latinime { @@ -33,9 +34,7 @@ class Ver4PatriciaTrieWritingHelper { Ver4PatriciaTrieWritingHelper(Ver4DictBuffers *const buffers) : mBuffers(buffers) {} - // TODO: Support counting ngram entries. - bool writeToDictFile(const char *const dictDirPath, const int unigramCount, - const int bigramCount) const; + bool writeToDictFile(const char *const dictDirPath, const EntryCounts &entryCounts) const; // This method cannot be const because the original dictionary buffer will be updated to detect // useless PtNodes during GC. @@ -68,8 +67,7 @@ class Ver4PatriciaTrieWritingHelper { }; bool runGC(const int rootPtNodeArrayPos, const HeaderPolicy *const headerPolicy, - Ver4DictBuffers *const buffersToWrite, int *const outUnigramCount, - int *const outBigramCount); + Ver4DictBuffers *const buffersToWrite, MutableEntryCounters *const outEntryCounters); Ver4DictBuffers *const mBuffers; }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp index b7e2a7278..edcb43678 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp @@ -27,6 +27,7 @@ #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" #include "suggest/policyimpl/dictionary/utils/file_utils.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" #include "utils/time_keeper.h" @@ -43,13 +44,13 @@ const int DictFileWritingUtils::SIZE_OF_BUFFER_SIZE_FIELD = 4; TimeKeeper::setCurrentTime(); const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::getFormatVersion(dictVersion); switch (formatVersion) { - case FormatUtils::VERSION_4: + case FormatUtils::VERSION_402: return createEmptyV4DictFile<backward::v402::Ver4DictConstants, backward::v402::Ver4DictBuffers, backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr>( filePath, localeAsCodePointVector, attributeMap, formatVersion); case FormatUtils::VERSION_4_ONLY_FOR_TESTING: - case FormatUtils::VERSION_4_DEV: + case FormatUtils::VERSION_403: return createEmptyV4DictFile<Ver4DictConstants, Ver4DictBuffers, Ver4DictBuffers::Ver4DictBuffersPtr>( filePath, localeAsCodePointVector, attributeMap, formatVersion); @@ -69,8 +70,7 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr> DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy, DictConstants::MAX_DICT_EXTENDED_REGION_SIZE); headerPolicy.fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */, - 0 /* unigramCount */, 0 /* bigramCount */, - 0 /* extendedRegionSize */, dictBuffers->getWritableHeaderBuffer()); + EntryCounts(), 0 /* extendedRegionSize */, dictBuffers->getWritableHeaderBuffer()); if (!DynamicPtWritingUtils::writeEmptyDictionary( dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) { AKLOGE("Empty ver4 dictionary structure cannot be created on memory."); diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/entry_counters.h b/native/jni/src/suggest/policyimpl/dictionary/utils/entry_counters.h new file mode 100644 index 000000000..73dc42a18 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/entry_counters.h @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#ifndef LATINIME_ENTRY_COUNTERS_H +#define LATINIME_ENTRY_COUNTERS_H + +#include <array> + +#include "defines.h" + +namespace latinime { + +// Copyable but immutable +class EntryCounts final { + public: + EntryCounts() : mEntryCounts({{0, 0, 0}}) {} + + EntryCounts(const int unigramCount, const int bigramCount, const int trigramCount) + : mEntryCounts({{unigramCount, bigramCount, trigramCount}}) {} + + explicit EntryCounts(const std::array<int, MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1> &counters) + : mEntryCounts(counters) {} + + int getUnigramCount() const { + return mEntryCounts[0]; + } + + int getBigramCount() const { + return mEntryCounts[1]; + } + + int getTrigramCount() const { + return mEntryCounts[2]; + } + + int getNgramCount(const size_t n) const { + if (n < 1 || n > mEntryCounts.size()) { + return 0; + } + return mEntryCounts[n - 1]; + } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(EntryCounts); + + const std::array<int, MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1> mEntryCounts; +}; + +class MutableEntryCounters final { + public: + MutableEntryCounters() { + mEntryCounters.fill(0); + } + + MutableEntryCounters(const int unigramCount, const int bigramCount, const int trigramCount) + : mEntryCounters({{unigramCount, bigramCount, trigramCount}}) {} + + const EntryCounts getEntryCounts() const { + return EntryCounts(mEntryCounters); + } + + int getUnigramCount() const { + return mEntryCounters[0]; + } + + int getBigramCount() const { + return mEntryCounters[1]; + } + + int getTrigramCount() const { + return mEntryCounters[2]; + } + + void incrementUnigramCount() { + ++mEntryCounters[0]; + } + + void decrementUnigramCount() { + ASSERT(mEntryCounters[0] != 0); + --mEntryCounters[0]; + } + + void incrementBigramCount() { + ++mEntryCounters[1]; + } + + void decrementBigramCount() { + ASSERT(mEntryCounters[1] != 0); + --mEntryCounters[1]; + } + + void incrementNgramCount(const size_t n) { + if (n < 1 || n > mEntryCounters.size()) { + return; + } + ++mEntryCounters[n - 1]; + } + + void decrementNgramCount(const size_t n) { + if (n < 1 || n > mEntryCounters.size()) { + return; + } + ASSERT(mEntryCounters[n - 1] != 0); + --mEntryCounters[n - 1]; + } + + void setNgramCount(const size_t n, const int count) { + if (n < 1 || n > mEntryCounters.size()) { + return; + } + mEntryCounters[n - 1] = count; + } + + private: + DISALLOW_COPY_AND_ASSIGN(MutableEntryCounters); + + std::array<int, MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1> mEntryCounters; +}; +} // namespace latinime +#endif /* LATINIME_ENTRY_COUNTERS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp index e5ef2abf8..9055f7bfc 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp @@ -38,8 +38,7 @@ const int ForgettingCurveUtils::OCCURRENCES_TO_RAISE_THE_LEVEL = 1; // 15 days const int ForgettingCurveUtils::DURATION_TO_LOWER_THE_LEVEL_IN_SECONDS = 15 * 24 * 60 * 60; -const float ForgettingCurveUtils::UNIGRAM_COUNT_HARD_LIMIT_WEIGHT = 1.2; -const float ForgettingCurveUtils::BIGRAM_COUNT_HARD_LIMIT_WEIGHT = 1.2; +const float ForgettingCurveUtils::ENTRY_COUNT_HARD_LIMIT_WEIGHT = 1.2; const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityTable; @@ -126,14 +125,22 @@ const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityT } /* static */ bool ForgettingCurveUtils::needsToDecay(const bool mindsBlockByDecay, - const int unigramCount, const int bigramCount, const HeaderPolicy *const headerPolicy) { - if (unigramCount >= getUnigramCountHardLimit(headerPolicy->getMaxUnigramCount())) { + const EntryCounts &entryCounts, const HeaderPolicy *const headerPolicy) { + if (entryCounts.getUnigramCount() + >= getEntryCountHardLimit(headerPolicy->getMaxUnigramCount())) { // Unigram count exceeds the limit. return true; - } else if (bigramCount >= getBigramCountHardLimit(headerPolicy->getMaxBigramCount())) { + } + if (entryCounts.getBigramCount() + >= getEntryCountHardLimit(headerPolicy->getMaxBigramCount())) { // Bigram count exceeds the limit. return true; } + if (entryCounts.getTrigramCount() + >= getEntryCountHardLimit(headerPolicy->getMaxTrigramCount())) { + // Trigram count exceeds the limit. + return true; + } if (mindsBlockByDecay) { return false; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h index ccbc4a98d..06dcae8a1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h @@ -21,6 +21,7 @@ #include "defines.h" #include "suggest/core/dictionary/property/historical_info.h" +#include "suggest/policyimpl/dictionary/utils/entry_counters.h" namespace latinime { @@ -42,22 +43,17 @@ class ForgettingCurveUtils { static bool needsToKeep(const HistoricalInfo *const historicalInfo, const HeaderPolicy *const headerPolicy); - static bool needsToDecay(const bool mindsBlockByDecay, const int unigramCount, - const int bigramCount, const HeaderPolicy *const headerPolicy); + static bool needsToDecay(const bool mindsBlockByDecay, const EntryCounts &entryCounters, + const HeaderPolicy *const headerPolicy); // TODO: Improve probability computation method and remove this. static int getProbabilityBiasForNgram(const int n) { return (n - 1) * MULTIPLIER_TWO_IN_PROBABILITY_SCALE; } - AK_FORCE_INLINE static int getUnigramCountHardLimit(const int maxUnigramCount) { - return static_cast<int>(static_cast<float>(maxUnigramCount) - * UNIGRAM_COUNT_HARD_LIMIT_WEIGHT); - } - - AK_FORCE_INLINE static int getBigramCountHardLimit(const int maxBigramCount) { - return static_cast<int>(static_cast<float>(maxBigramCount) - * BIGRAM_COUNT_HARD_LIMIT_WEIGHT); + AK_FORCE_INLINE static int getEntryCountHardLimit(const int maxEntryCount) { + return static_cast<int>(static_cast<float>(maxEntryCount) + * ENTRY_COUNT_HARD_LIMIT_WEIGHT); } private: @@ -101,8 +97,7 @@ class ForgettingCurveUtils { static const int OCCURRENCES_TO_RAISE_THE_LEVEL; static const int DURATION_TO_LOWER_THE_LEVEL_IN_SECONDS; - static const float UNIGRAM_COUNT_HARD_LIMIT_WEIGHT; - static const float BIGRAM_COUNT_HARD_LIMIT_WEIGHT; + static const float ENTRY_COUNT_HARD_LIMIT_WEIGHT; static const ProbabilityTable sProbabilityTable; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp index 0cffe569d..e225c235e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp @@ -28,15 +28,17 @@ const size_t FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; /* static */ FormatUtils::FORMAT_VERSION FormatUtils::getFormatVersion(const int formatVersion) { switch (formatVersion) { case VERSION_2: - return VERSION_2; case VERSION_201: - return VERSION_201; + AKLOGE("Dictionary versions 2 and 201 are incompatible with this version"); + return UNKNOWN_VERSION; + case VERSION_202: + return VERSION_202; case VERSION_4_ONLY_FOR_TESTING: return VERSION_4_ONLY_FOR_TESTING; - case VERSION_4: - return VERSION_4; - case VERSION_4_DEV: - return VERSION_4_DEV; + case VERSION_402: + return VERSION_402; + case VERSION_403: + return VERSION_403; default: return UNKNOWN_VERSION; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h index 96310086b..1616efcce 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h @@ -31,11 +31,15 @@ class FormatUtils { public: enum FORMAT_VERSION { // These MUST have the same values as the relevant constants in FormatSpec.java. + // TODO: Remove VERSION_2 and VERSION_201 when we: + // * Confirm that old versions of LatinIME download old-format dictionaries + // * We no longer need the corresponding constants on the Java side for dicttool VERSION_2 = 2, VERSION_201 = 201, + VERSION_202 = 202, VERSION_4_ONLY_FOR_TESTING = 399, - VERSION_4 = 402, - VERSION_4_DEV = 403, + VERSION_402 = 402, + VERSION_403 = 403, UNKNOWN_VERSION = -1 }; diff --git a/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.cpp index 27b2f5012..e8fa06942 100644 --- a/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.cpp @@ -14,30 +14,10 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" -import android.content.Context; -import android.view.KeyEvent; +namespace latinime { -final class SpecialKeyDetector { - /** - * Special physical key detector - * @param context a context of this detector. - */ - public SpecialKeyDetector(final Context context) { - } +const float ProbabilityUtils::PROBABILITY_ENCODING_SCALER = 8.58923700372f; - /** - * Record a down key event. - * @param keyEvent a down key event. - */ - public void onKeyDown(final KeyEvent keyEvent) { - } - - /** - * Record an up key event. - * @param keyEvent an up key event. - */ - public void onKeyUp(final KeyEvent keyEvent) { - } -} +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h index 3b339e61a..2050af1e9 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h @@ -17,6 +17,9 @@ #ifndef LATINIME_PROBABILITY_UTILS_H #define LATINIME_PROBABILITY_UTILS_H +#include <algorithm> +#include <cmath> + #include "defines.h" namespace latinime { @@ -47,8 +50,20 @@ class ProbabilityUtils { + static_cast<int>(static_cast<float>(bigramProbability + 1) * stepSize); } + // Encode probability using the same way as we are doing for main dictionaries. + static AK_FORCE_INLINE int encodeRawProbability(const float rawProbability) { + const float probability = static_cast<float>(MAX_PROBABILITY) + + log2f(rawProbability) * PROBABILITY_ENCODING_SCALER; + if (probability < 0.0f) { + return 0; + } + return std::min(static_cast<int>(probability + 0.5f), MAX_PROBABILITY); + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(ProbabilityUtils); + + static const float PROBABILITY_ENCODING_SCALER; }; } #endif /* LATINIME_PROBABILITY_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp index 6a2db687d..856808a74 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp @@ -24,6 +24,7 @@ const int ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED = 120; const float ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD = 1.0f; const float ScoringParams::EXACT_MATCH_PROMOTION = 1.1f; +const float ScoringParams::PERFECT_MATCH_PROMOTION = 1.1f; const float ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH = 0.01f; const float ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH = 0.02f; const float ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH = 0.03f; @@ -48,17 +49,17 @@ const float ScoringParams::INSERTION_COST_SAME_CHAR = 0.5508f; const float ScoringParams::INSERTION_COST_PROXIMITY_CHAR = 0.674f; const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.639f; const float ScoringParams::TRANSPOSITION_COST = 0.5608f; -const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.334f; +const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.33f; +const float ScoringParams::SPACE_OMISSION_COST = 0.1f; const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.37972f; const float ScoringParams::SUBSTITUTION_COST = 0.3806f; -const float ScoringParams::COST_NEW_WORD = 0.0314f; const float ScoringParams::COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE = 0.3224f; const float ScoringParams::DISTANCE_WEIGHT_LANGUAGE = 1.1214f; const float ScoringParams::COST_FIRST_COMPLETION = 0.4836f; const float ScoringParams::COST_COMPLETION = 0.00624f; const float ScoringParams::HAS_PROXIMITY_TERMINAL_COST = 0.0683f; const float ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST = 0.0362f; -const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.4182f; +const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.3482f; const float ScoringParams::TYPING_BASE_OUTPUT_SCORE = 1.0f; const float ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT = 0.1f; const float ScoringParams::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT = 0.095f; diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h index 731424f3d..6f327a370 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.h +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h @@ -34,6 +34,7 @@ class ScoringParams { static const int THRESHOLD_SHORT_WORD_LENGTH; static const float EXACT_MATCH_PROMOTION; + static const float PERFECT_MATCH_PROMOTION; static const float CASE_ERROR_PENALTY_FOR_EXACT_MATCH; static const float ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH; static const float DIGRAPH_PENALTY_FOR_EXACT_MATCH; @@ -56,9 +57,9 @@ class ScoringParams { static const float INSERTION_COST_FIRST_CHAR; static const float TRANSPOSITION_COST; static const float SPACE_SUBSTITUTION_COST; + static const float SPACE_OMISSION_COST; static const float ADDITIONAL_PROXIMITY_COST; static const float SUBSTITUTION_COST; - static const float COST_NEW_WORD; static const float COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE; static const float DISTANCE_WEIGHT_LANGUAGE; static const float COST_FIRST_COMPLETION; diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h index 0240bcf54..6acd767ea 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h @@ -44,23 +44,50 @@ class TypingScoring : public Scoring { AK_FORCE_INLINE int calculateFinalScore(const float compoundDistance, const int inputSize, const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit, - const bool boostExactMatches) const { + const bool boostExactMatches, const bool hasProbabilityZero) const { const float maxDistance = ScoringParams::DISTANCE_WEIGHT_LANGUAGE + static_cast<float>(inputSize) * ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT; float score = ScoringParams::TYPING_BASE_OUTPUT_SCORE - compoundDistance / maxDistance; if (forceCommit) { score += ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD; } - if (boostExactMatches && ErrorTypeUtils::isExactMatch(containedErrorTypes)) { - score += ScoringParams::EXACT_MATCH_PROMOTION; - if ((ErrorTypeUtils::MATCH_WITH_WRONG_CASE & containedErrorTypes) != 0) { - score -= ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH; + if (hasProbabilityZero) { + // Previously, when both legitimate 0-frequency words (such as distracters) and + // offensive words were encoded in the same way, distracters would never show up + // when the user blocked offensive words (the default setting, as well as the + // setting for regression tests). + // + // When b/11031090 was fixed and a separate encoding was used for offensive words, + // 0-frequency words would no longer be blocked when they were an "exact match" + // (where case mismatches and accent mismatches would be considered an "exact + // match"). The exact match boosting functionality meant that, for example, when + // the user typed "mt" they would be suggested the word "Mt", although they most + // probably meant to type "my". + // + // For this reason, we introduced this change, which does the following: + // * Defines the "perfect match" as a really exact match, with no room for case or + // accent mismatches + // * When the target word has probability zero (as "Mt" does, because it is a + // distracter), ONLY boost its score if it is a perfect match. + // + // By doing this, when the user types "mt", the word "Mt" will NOT be boosted, and + // they will get "my". However, if the user makes an explicit effort to type "Mt", + // we do boost the word "Mt" so that the user's input is not autocorrected to "My". + if (boostExactMatches && ErrorTypeUtils::isPerfectMatch(containedErrorTypes)) { + score += ScoringParams::PERFECT_MATCH_PROMOTION; } - if ((ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT & containedErrorTypes) != 0) { - score -= ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH; - } - if ((ErrorTypeUtils::MATCH_WITH_DIGRAPH & containedErrorTypes) != 0) { - score -= ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH; + } else { + if (boostExactMatches && ErrorTypeUtils::isExactMatch(containedErrorTypes)) { + score += ScoringParams::EXACT_MATCH_PROMOTION; + if ((ErrorTypeUtils::MATCH_WITH_WRONG_CASE & containedErrorTypes) != 0) { + score -= ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH; + } + if ((ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT & containedErrorTypes) != 0) { + score -= ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH; + } + if ((ErrorTypeUtils::MATCH_WITH_DIGRAPH & containedErrorTypes) != 0) { + score -= ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH; + } } } return static_cast<int>(score * SUGGEST_INTERFACE_OUTPUT_SCALE); diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h index 84077174d..1338ac81a 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h @@ -150,9 +150,10 @@ class TypingWeighting : public Weighting { return cost + weightedDistance; } - float getNewWordSpatialCost(const DicTraverseSession *const traverseSession, + float getSpaceOmissionCost(const DicTraverseSession *const traverseSession, const DicNode *const dicNode, DicNode_InputStateG *inputStateG) const { - return ScoringParams::COST_NEW_WORD * traverseSession->getMultiWordCostMultiplier(); + const float cost = ScoringParams::SPACE_OMISSION_COST; + return cost * traverseSession->getMultiWordCostMultiplier(); } float getNewWordBigramLanguageCost(const DicTraverseSession *const traverseSession, @@ -202,7 +203,10 @@ class TypingWeighting : public Weighting { AK_FORCE_INLINE float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const { - const float cost = ScoringParams::SPACE_SUBSTITUTION_COST + ScoringParams::COST_NEW_WORD; + const int inputIndex = dicNode->getInputIndex(0); + const float distanceToSpaceKey = traverseSession->getProximityInfoState(0) + ->getPointToKeyLength(inputIndex, KEYCODE_SPACE); + const float cost = ScoringParams::SPACE_SUBSTITUTION_COST * distanceToSpaceKey; return cost * traverseSession->getMultiWordCostMultiplier(); } diff --git a/native/jni/src/utils/char_utils.h b/native/jni/src/utils/char_utils.h index 5e9cdd9b2..7871c26ef 100644 --- a/native/jni/src/utils/char_utils.h +++ b/native/jni/src/utils/char_utils.h @@ -101,6 +101,17 @@ class CharUtils { return codePointCount + 1; } + // Returns updated code point count. + static AK_FORCE_INLINE int removeBeginningOfSentenceMarker(int *const codePoints, + const int codePointCount) { + if (codePointCount <= 0 || codePoints[0] != CODE_POINT_BEGINNING_OF_SENTENCE) { + return codePointCount; + } + const int newCodePointCount = codePointCount - 1; + memmove(codePoints, codePoints + 1, sizeof(int) * newCodePointCount); + return newCodePointCount; + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(CharUtils); diff --git a/native/jni/src/utils/jni_data_utils.h b/native/jni/src/utils/jni_data_utils.h index 25cc41742..a259e1cd0 100644 --- a/native/jni/src/utils/jni_data_utils.h +++ b/native/jni/src/utils/jni_data_utils.h @@ -50,6 +50,7 @@ class JniDataUtils { const jsize keyUtf8Length = env->GetStringUTFLength(keyString); char keyChars[keyUtf8Length + 1]; env->GetStringUTFRegion(keyString, 0, env->GetStringLength(keyString), keyChars); + env->DeleteLocalRef(keyString); keyChars[keyUtf8Length] = '\0'; DictionaryHeaderStructurePolicy::AttributeMap::key_type key; HeaderReadWriteUtils::insertCharactersIntoVector(keyChars, &key); @@ -59,6 +60,7 @@ class JniDataUtils { const jsize valueUtf8Length = env->GetStringUTFLength(valueString); char valueChars[valueUtf8Length + 1]; env->GetStringUTFRegion(valueString, 0, env->GetStringLength(valueString), valueChars); + env->DeleteLocalRef(valueString); valueChars[valueUtf8Length] = '\0'; DictionaryHeaderStructurePolicy::AttributeMap::mapped_type value; HeaderReadWriteUtils::insertCharactersIntoVector(valueChars, &value); @@ -113,6 +115,7 @@ class JniDataUtils { continue; } env->GetIntArrayRegion(prevWord, 0, prevWordLength, prevWordCodePoints[i]); + env->DeleteLocalRef(prevWord); prevWordCodePointCount[i] = prevWordLength; jboolean isBeginningOfSentenceBoolean = JNI_FALSE; env->GetBooleanArrayRegion(isBeginningOfSentenceArray, i, 1 /* len */, diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp new file mode 100644 index 000000000..44b5a8aaa --- /dev/null +++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters_test.cpp @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_global_counters.h" + +#include <gtest/gtest.h> + +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" + +namespace latinime { +namespace { + +TEST(LanguageModelDictContentGlobalCountersTest, TestUpdateMaxValueOfCounters) { + LanguageModelDictContentGlobalCounters globalCounters; + + EXPECT_FALSE(globalCounters.needsToHalveCounters()); + globalCounters.updateMaxValueOfCounters(10); + EXPECT_FALSE(globalCounters.needsToHalveCounters()); + const int count = (1 << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT)) - 1; + globalCounters.updateMaxValueOfCounters(count); + EXPECT_TRUE(globalCounters.needsToHalveCounters()); + globalCounters.halveCounters(); + EXPECT_FALSE(globalCounters.needsToHalveCounters()); +} + +TEST(LanguageModelDictContentGlobalCountersTest, TestIncrementTotalCount) { + LanguageModelDictContentGlobalCounters globalCounters; + + EXPECT_EQ(0, globalCounters.getTotalCount()); + globalCounters.incrementTotalCount(); + EXPECT_EQ(1, globalCounters.getTotalCount()); + for (int i = 1; i < 50; ++i) { + globalCounters.incrementTotalCount(); + } + EXPECT_EQ(50, globalCounters.getTotalCount()); + globalCounters.halveCounters(); + EXPECT_EQ(25, globalCounters.getTotalCount()); + globalCounters.halveCounters(); + EXPECT_EQ(12, globalCounters.getTotalCount()); + for (int i = 0; i < 4; ++i) { + globalCounters.halveCounters(); + } + EXPECT_EQ(0, globalCounters.getTotalCount()); +} + +} // namespace +} // namespace latinime diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp index 4469dc715..313a9af10 100644 --- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp +++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp @@ -52,16 +52,14 @@ TEST(LanguageModelDictContentTest, TestUnigramProbabilityWithHistoricalInfo) { const int flag = 0xF0; const int timestamp = 0x3FFFFFFF; - const int level = 3; const int count = 10; const int wordId = 100; - const HistoricalInfo historicalInfo(timestamp, level, count); + const HistoricalInfo historicalInfo(timestamp, 0 /* level */, count); const ProbabilityEntry probabilityEntry(flag, &historicalInfo); languageModelDictContent.setProbabilityEntry(wordId, &probabilityEntry); const ProbabilityEntry entry = languageModelDictContent.getProbabilityEntry(wordId); EXPECT_EQ(flag, entry.getFlags()); EXPECT_EQ(timestamp, entry.getHistoricalInfo()->getTimestamp()); - EXPECT_EQ(level, entry.getHistoricalInfo()->getLevel()); EXPECT_EQ(count, entry.getHistoricalInfo()->getCount()); // Remove @@ -108,14 +106,14 @@ TEST(LanguageModelDictContentTest, TestGetWordProbability) { languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(1), wordId, &bigramProbabilityEntry); EXPECT_EQ(bigramProbability, languageModelDictContent.getWordAttributes(prevWordIds, wordId, - nullptr /* headerPolicy */).getProbability()); + false /* mustMatchAllPrevWords */, nullptr /* headerPolicy */).getProbability()); const ProbabilityEntry trigramProbabilityEntry(flag, trigramProbability); languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(1), prevWordIds[1], &probabilityEntry); languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(2), wordId, &trigramProbabilityEntry); EXPECT_EQ(trigramProbability, languageModelDictContent.getWordAttributes(prevWordIds, wordId, - nullptr /* headerPolicy */).getProbability()); + false /* mustMatchAllPrevWords */, nullptr /* headerPolicy */).getProbability()); } } // namespace diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp index 260b347ce..eb78034ba 100644 --- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp +++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp @@ -39,20 +39,18 @@ TEST(ProbabilityEntryTest, TestEncodeDecode) { TEST(ProbabilityEntryTest, TestEncodeDecodeWithHistoricalInfo) { const int flag = 0xF0; const int timestamp = 0x3FFFFFFF; - const int level = 3; - const int count = 10; + const int count = 0xABCD; - const HistoricalInfo historicalInfo(timestamp, level, count); + const HistoricalInfo historicalInfo(timestamp, 0 /* level */, count); const ProbabilityEntry entry(flag, &historicalInfo); const uint64_t encodedEntry = entry.encode(true /* hasHistoricalInfo */); - EXPECT_EQ(0xF03FFFFFFF030Aull, encodedEntry); + EXPECT_EQ(0xF03FFFFFFFABCDull, encodedEntry); const ProbabilityEntry decodedEntry = ProbabilityEntry::decode(encodedEntry, true /* hasHistoricalInfo */); EXPECT_EQ(flag, decodedEntry.getFlags()); EXPECT_EQ(timestamp, decodedEntry.getHistoricalInfo()->getTimestamp()); - EXPECT_EQ(level, decodedEntry.getHistoricalInfo()->getLevel()); EXPECT_EQ(count, decodedEntry.getHistoricalInfo()->getCount()); } diff --git a/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp index 15f560cd1..494200568 100644 --- a/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp +++ b/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp @@ -62,14 +62,14 @@ TEST(FormatUtilsTest, TestDetectFormatVersion) { } { const std::vector<uint8_t> buffer = - getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_4, 0, 0); - EXPECT_EQ(FormatUtils::VERSION_4, FormatUtils::detectFormatVersion( + getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_402, 0, 0); + EXPECT_EQ(FormatUtils::VERSION_402, FormatUtils::detectFormatVersion( ReadOnlyByteArrayView(buffer.data(), buffer.size()))); } { const std::vector<uint8_t> buffer = - getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_4_DEV, 0, 0); - EXPECT_EQ(FormatUtils::VERSION_4_DEV, FormatUtils::detectFormatVersion( + getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_403, 0, 0); + EXPECT_EQ(FormatUtils::VERSION_403, FormatUtils::detectFormatVersion( ReadOnlyByteArrayView(buffer.data(), buffer.size()))); } diff --git a/native/jni/tests/suggest/policyimpl/dictionary/utils/probability_utils_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/utils/probability_utils_test.cpp new file mode 100644 index 000000000..be1f278c6 --- /dev/null +++ b/native/jni/tests/suggest/policyimpl/dictionary/utils/probability_utils_test.cpp @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" + +#include <gtest/gtest.h> + +#include "defines.h" + +namespace latinime { +namespace { + +TEST(ProbabilityUtilsTest, TestEncodeRawProbability) { + EXPECT_EQ(MAX_PROBABILITY, ProbabilityUtils::encodeRawProbability(1.0f)); + EXPECT_EQ(MAX_PROBABILITY - 9, ProbabilityUtils::encodeRawProbability(0.5f)); + EXPECT_EQ(0, ProbabilityUtils::encodeRawProbability(0.0f)); +} + +} // namespace +} // namespace latinime diff --git a/tests/Android.mk b/tests/Android.mk index a084ad10d..7cdabf249 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -24,7 +24,7 @@ LOCAL_AAPT_FLAGS += -0 .dict # Do not compress test data file LOCAL_AAPT_FLAGS += -0 .txt -LOCAL_STATIC_JAVA_LIBRARIES := mockito-target android-support-test +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java b/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java index 319302c71..67e76464b 100644 --- a/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java +++ b/tests/src/com/android/inputmethod/compat/LocaleSpanCompatUtilsTests.java @@ -20,7 +20,6 @@ import android.graphics.Typeface; import android.os.Build; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; import android.text.style.StyleSpan; @@ -72,141 +71,141 @@ public class LocaleSpanCompatUtilsTests extends AndroidTestCase { final SpannableString text = new SpannableString("0123456789"); LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE); assertSpanCount(1, text); - assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if only LocaleSpans are updated. { final SpannableString text = new SpannableString("0123456789"); final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD); - text.setSpan(styleSpan, 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(styleSpan, 0, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE); assertSpanCount(2, text); assertSpanEquals(styleSpan, text, 0); - assertLocaleSpan(text, 1, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 1, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if two jointed spans are merged into one span. { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE); assertSpanCount(1, text); - assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if two overlapped spans are merged into one span. { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE); assertSpanCount(1, text); - assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if three overlapped spans are merged into one span. { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 8, Locale.JAPANESE); assertSpanCount(1, text); - assertLocaleSpan(text, 0, 1, 8, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 1, 8, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if disjoint spans remain disjoint. { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 8, 9, Locale.JAPANESE); assertSpanCount(3, text); - assertLocaleSpan(text, 0, 1, 3, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - assertLocaleSpan(text, 1, 5, 6, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - assertLocaleSpan(text, 2, 8, 9, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 1, 3, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 1, 5, 6, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 2, 8, 9, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if existing span flags are preserved during merge. { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5, - Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE); + Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE); LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 4, Locale.JAPANESE); assertSpanCount(1, text); assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, - Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE); + Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE); } // Test if existing span flags are preserved even when partially overlapped (leading edge). { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5, - Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE); + Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE); LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 7, Locale.JAPANESE); assertSpanCount(1, text); assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE, - Spannable.SPAN_INCLUSIVE_EXCLUSIVE | Spannable.SPAN_INTERMEDIATE); + Spanned.SPAN_INCLUSIVE_EXCLUSIVE | Spanned.SPAN_INTERMEDIATE); } // Test if existing span flags are preserved even when partially overlapped (trailing edge). { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 3, 7, - Spannable.SPAN_INCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE); + Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE); LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE); assertSpanCount(1, text); assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE, - Spannable.SPAN_EXCLUSIVE_INCLUSIVE | Spannable.SPAN_INTERMEDIATE); + Spanned.SPAN_EXCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE); } // Test if existing locale span will be removed when the locale doesn't match. { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 5, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 7, Locale.JAPANESE); assertSpanCount(1, text); - assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if existing locale span will be removed when the locale doesn't match. (case 2) { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 6, Locale.JAPANESE); assertSpanCount(3, text); - assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - assertLocaleSpan(text, 1, 6, 7, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - assertLocaleSpan(text, 2, 5, 6, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 1, 6, 7, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 2, 5, 6, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if existing locale span will be removed when the locale doesn't match. (case 3) { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 5, Locale.JAPANESE); assertSpanCount(2, text); - assertLocaleSpan(text, 0, 5, 7, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - assertLocaleSpan(text, 1, 2, 5, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 5, 7, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 1, 2, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Test if existing locale span will be removed when the locale doesn't match. (case 3) { final SpannableString text = new SpannableString("0123456789"); text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 8, Locale.JAPANESE); assertSpanCount(2, text); - assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - assertLocaleSpan(text, 1, 5, 8, Locale.JAPANESE, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertLocaleSpan(text, 1, 5, 8, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysBase.java index 8a55455d0..21333b0a0 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysBase.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysBase.java @@ -22,9 +22,9 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.MoreKeySpec; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Arrays; diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java index 71bbdba1a..0246c49a1 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java @@ -17,9 +17,7 @@ package com.android.inputmethod.keyboard; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; -import android.preference.PreferenceManager; import android.test.AndroidTestCase; import android.view.ContextThemeWrapper; import android.view.inputmethod.EditorInfo; @@ -28,10 +26,10 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.keyboard.KeyboardLayoutSet.Builder; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.ResourceUtils; @@ -62,8 +60,8 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { } }; - private SharedPreferences mSharedPreferences; - private String mSavedAdditionalSubtypes; + private RichInputMethodManager mRichImm; + private InputMethodSubtype[] mSavedAdditionalSubtypes; private int mScreenMetrics; protected abstract int getKeyboardThemeForTests(); @@ -72,26 +70,26 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); final Context context = getContext(); - mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); final Resources res = context.getResources(); + RichInputMethodManager.init(context); + mRichImm = RichInputMethodManager.getInstance(); - // Save additional subtypes preference. - mSavedAdditionalSubtypes = Settings.readPrefAdditionalSubtypes(mSharedPreferences, res); - final String predefinedSubtypes = AdditionalSubtypeUtils.createPrefSubtypes( - res.getStringArray(R.array.predefined_subtypes)); - // Reset additional subtypes to predefined ones. - Settings.writePrefAdditionalSubtypes(mSharedPreferences, predefinedSubtypes); + // Save and reset additional subtypes preference. + mSavedAdditionalSubtypes = mRichImm.getAdditionalSubtypes(context); + final InputMethodSubtype[] predefinedAdditionalSubtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray( + AdditionalSubtypeUtils.createPrefSubtypes( + res.getStringArray(R.array.predefined_subtypes))); + mRichImm.setAdditionalInputMethodSubtypes(predefinedAdditionalSubtypes); final KeyboardTheme keyboardTheme = KeyboardTheme.searchKeyboardThemeById( getKeyboardThemeForTests(), KeyboardTheme.KEYBOARD_THEMES); setContext(new ContextThemeWrapper(getContext(), keyboardTheme.mStyleId)); KeyboardLayoutSet.onKeyboardThemeChanged(); - mScreenMetrics = res.getInteger(R.integer.config_screen_metrics); - RichInputMethodManager.init(context); - final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); + mScreenMetrics = Settings.readScreenMetrics(res); - final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme(); + final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme(); final int subtypeCount = imi.getSubtypeCount(); for (int index = 0; index < subtypeCount; index++) { mAllSubtypesList.add(imi.getSubtypeAt(index)); @@ -101,7 +99,7 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { @Override protected void tearDown() throws Exception { // Restore additional subtypes preference. - Settings.writePrefAdditionalSubtypes(mSharedPreferences, mSavedAdditionalSubtypes); + mRichImm.setAdditionalInputMethodSubtypes(mSavedAdditionalSubtypes); super.tearDown(); } @@ -121,8 +119,7 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { } protected final boolean isPhone() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE; + return Constants.isPhone(mScreenMetrics); } protected final InputMethodSubtype getSubtype(final Locale locale, diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java index d642a1073..33e88c13b 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java @@ -16,13 +16,13 @@ package com.android.inputmethod.keyboard; -import static com.android.inputmethod.compat.BuildCompatUtils.VERSION_CODES_LXX; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_ICS; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_KLP; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_LXX_DARK; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_LXX_LIGHT; import android.content.SharedPreferences; +import android.os.Build; import android.os.Build.VERSION_CODES; import android.preference.PreferenceManager; import android.test.AndroidTestCase; @@ -131,7 +131,7 @@ public class KeyboardThemeTests extends AndroidTestCase { } public void testKeyboardThemePreferenceOnLxx() { - assertKeyboardThemePreferenceOnLxx(VERSION_CODES_LXX); + assertKeyboardThemePreferenceOnLxx(Build.VERSION_CODES.LOLLIPOP); } /* @@ -184,7 +184,7 @@ public class KeyboardThemeTests extends AndroidTestCase { } public void testDefaultKeyboardThemeOnLxx() { - assertDefaultKeyboardThemeOnLxx(VERSION_CODES_LXX); + assertDefaultKeyboardThemeOnLxx(Build.VERSION_CODES.LOLLIPOP); } /* @@ -251,7 +251,7 @@ public class KeyboardThemeTests extends AndroidTestCase { // Upgrading keyboard on L. public void testUpgradeKeyboardToLxxOnLxx() { - assertUpgradeKeyboardToLxxOnLxx(VERSION_CODES_LXX); + assertUpgradeKeyboardToLxxOnLxx(Build.VERSION_CODES.LOLLIPOP); } /* @@ -304,7 +304,7 @@ public class KeyboardThemeTests extends AndroidTestCase { private void assertUpgradePlatformToLxxFrom(final int oldSdkVersion) { // Forced to switch to LXX theme. - final int newSdkVersion = VERSION_CODES_LXX; + final int newSdkVersion = Build.VERSION_CODES.LOLLIPOP; assertUpgradePlatformFromTo( oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_LXX_LIGHT); assertUpgradePlatformFromTo( @@ -329,8 +329,8 @@ public class KeyboardThemeTests extends AndroidTestCase { // Update platform from L to L. public void testUpgradePlatformToLxxFromLxx() { - final int oldSdkVersion = VERSION_CODES_LXX; - final int newSdkVersion = VERSION_CODES_LXX; + final int oldSdkVersion = Build.VERSION_CODES.LOLLIPOP; + final int newSdkVersion = Build.VERSION_CODES.LOLLIPOP; assertUpgradePlatformFromTo( oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_LXX_LIGHT); assertUpgradePlatformFromTo( @@ -419,7 +419,7 @@ public class KeyboardThemeTests extends AndroidTestCase { setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL); setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); - final int sdkVersion = VERSION_CODES_LXX; + final int sdkVersion = Build.VERSION_CODES.LOLLIPOP; final String oldPrefKey = KeyboardTheme.getPreferenceKey(sdkVersion); setKeyboardThemePreference(oldPrefKey, THEME_ID_LXX_DARK); diff --git a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java index 41b545aa8..94caf51ed 100644 --- a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java @@ -29,7 +29,7 @@ import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyVisual; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java index 8e26e7fc7..1cdc78799 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java @@ -17,11 +17,11 @@ package com.android.inputmethod.keyboard.internal; import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; -import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; +import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; @SmallTest public final class KeySpecParserTests extends KeySpecParserTestsBase { diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java index b8cb11b6b..79cf10e84 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java @@ -19,8 +19,8 @@ package com.android.inputmethod.keyboard.internal; import static com.android.inputmethod.keyboard.internal.KeyboardCodesSet.PREFIX_CODE; import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.PREFIX_ICON; -import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT; -import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; +import static com.android.inputmethod.latin.common.Constants.CODE_OUTPUT_TEXT; +import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED; import android.test.AndroidTestCase; diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java index 3ffd0a96a..1474c8d27 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java @@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.internal; import android.test.AndroidTestCase; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; public class KeyboardStateTestsBase extends AndroidTestCase implements MockKeyboardSwitcher.MockConstants { diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java index 986a233c1..4b2ec9588 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java @@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.text.TextUtils; import com.android.inputmethod.event.Event; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.RecapitalizeStatus; public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java index 6c0d74941..d9e5d1033 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java @@ -17,11 +17,11 @@ package com.android.inputmethod.keyboard.internal; import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; -import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; +import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Arrays; import java.util.Locale; @@ -78,6 +78,7 @@ public final class MoreKeySpecTests extends KeySpecParserTestsBase { assertArrayEquals(message, expected, actual); } + @SuppressWarnings("static-method") public void testEmptyEntry() { assertInsertAdditionalMoreKeys("null more keys and null additons", null, @@ -106,6 +107,7 @@ public final class MoreKeySpecTests extends KeySpecParserTestsBase { new String[] { "a", "A", "b", "B", "c", "d" }); } + @SuppressWarnings("static-method") public void testInsertAdditionalMoreKeys() { // Escaped marker. assertInsertAdditionalMoreKeys("escaped marker", @@ -306,6 +308,7 @@ public final class MoreKeySpecTests extends KeySpecParserTestsBase { assertArrayEquals(message, expected, actual); } + @SuppressWarnings("static-method") public void testGetBooleanValue() { assertGetBooleanValue("Has label", HAS_LABEL, new String[] { HAS_LABEL, "a", "b", "c" }, @@ -345,6 +348,7 @@ public final class MoreKeySpecTests extends KeySpecParserTestsBase { assertArrayEquals(message, expected, actual); } + @SuppressWarnings("static-method") public void testGetIntValue() { assertGetIntValue("Fixed column order 3", FIXED_COLUMN_ORDER, -1, new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java index 9c2efe246..ff05f92c2 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java @@ -21,7 +21,7 @@ import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java index 261618f44..cbbeff4a7 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java @@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard.layout; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java index 7c75a3ee9..6dc4559dc 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java @@ -21,7 +21,7 @@ import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java b/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java index c26cb9673..d1ac5fd6e 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java @@ -20,7 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Greek.java b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java index a8eb3d942..0209c2ae1 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Greek.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java @@ -20,7 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java b/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java index 69b43588f..1b91b47ae 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java @@ -21,7 +21,7 @@ import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java index 4fff577d3..5a8d32cb8 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java @@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard.layout; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java index 20c4d0386..4d82f090b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java @@ -20,7 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java index 091c3a611..149f75397 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java @@ -20,7 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java b/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java index 55c2e8b98..3497c356f 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java @@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard.layout; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java index f7b3590f3..026e70118 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java @@ -20,7 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java index cdd9ea7ae..c3a9351f7 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java @@ -20,7 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java index 84c5df622..81437f3ac 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java @@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard.layout; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; @@ -140,8 +140,8 @@ public final class Telugu extends LayoutBase { key("\u0C2A", moreKey("\u0C2B")), // U+0C30: "ర" TELUGU LETTER RA // U+0C31: "ఱ" TELUGU LETTER RRA - // U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R - key("\u0C30", joinMoreKeys("\u0C31", "\u0C43")), + // U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA + key("\u0C30", joinMoreKeys("\u0C31", "\u0C4D\u0C30")), // U+0C15: "క" TELUGU LETTER KA // U+0C16: "ఖ" TELUGU LETTER KHA key("\u0C15", moreKey("\u0C16")), @@ -155,18 +155,21 @@ public final class Telugu extends LayoutBase { // U+0C20: "ఠ" TELUGU LETTER TTHA key("\u0C1F", moreKey("\u0C20"))) .setKeysOfRow(3, - // U+0C46: "ె" TELUGU VOWEL SIGN E + // U+0C4A: "ొ" TELUGU VOWEL SIGN O // U+0C12: "ఒ" TELUGU LETTER O - key("\u0C46", moreKey("\u0C12")), - // U+0C02: "ం" TELUGU SIGN ANUSVARA + key("\u0C4A", moreKey("\u0C12")), + // U+0C46: "ె" TELUGU VOWEL SIGN E // U+0C0E: "ఎ" TELUGU LETTER E - key("\u0C02", moreKey("\u0C0E")), + key("\u0C46", moreKey("\u0C0E")), // U+0C2E: "మ" TELUGU LETTER MA - "\u0C2E", + // U+0C02: "ం" TELUGU SIGN ANUSVARA + // U+0C01: "ఁ" TELUGU SIGN CANDRABINDU + key("\u0C2E", joinMoreKeys("\u0C02", "\u0C01")), // U+0C28: "న" TELUGU LETTER NA // U+0C23: "ణ" TELUGU LETTER NNA // U+0C19: "ఙ" TELUGU LETTER NGA - key("\u0C28", joinMoreKeys("\u0C23", "\u0C19")), + // U+0C1E: "ఞ" TELUGU LETTER NYA + key("\u0C28", joinMoreKeys("\u0C23", "\u0C19", "\u0C1E")), // U+0C35: "వ" TELUGU LETTER VA "\u0C35", // U+0C32: "ల" TELUGU LETTER LA @@ -176,8 +179,8 @@ public final class Telugu extends LayoutBase { // U+0C36: "శ" TELUGU LETTER SHA key("\u0C38", moreKey("\u0C36")), // U+0C0B: "ఋ" TELUGU LETTER VOCALIC R - // U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA - key("\u0C0B", moreKey("\u0C4D\u0C30")), + // U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R + key("\u0C0B", moreKey("\u0C43")), // U+0C37: "ష" TELUGU LETTER SSA // U+0C15/U+0C4D/U+0C37: // "క్ష" TELUGU LETTER KA/TELUGU SIGN VIRAMA/TELUGU LETTER SSA diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java index 1463336c4..ce5fd8068 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java @@ -20,7 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/BengaliCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/BengaliCustomizer.java index d255516b6..f13c26114 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/customizer/BengaliCustomizer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/BengaliCustomizer.java @@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.layout.customizer; import com.android.inputmethod.keyboard.layout.SymbolsShifted; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/DevanagariCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DevanagariCustomizer.java index d4e5e5885..13f9171d4 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/customizer/DevanagariCustomizer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DevanagariCustomizer.java @@ -17,7 +17,7 @@ package com.android.inputmethod.keyboard.layout.customizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/EastSlavicCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EastSlavicCustomizer.java index 03fc973f0..8815b068c 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/customizer/EastSlavicCustomizer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EastSlavicCustomizer.java @@ -17,7 +17,7 @@ package com.android.inputmethod.keyboard.layout.customizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/SouthSlavicLayoutCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SouthSlavicLayoutCustomizer.java index cc41d37ca..bec816000 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/customizer/SouthSlavicLayoutCustomizer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SouthSlavicLayoutCustomizer.java @@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.layout.customizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/TamilCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/TamilCustomizer.java index 91fd21ef5..de82aaf9e 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/customizer/TamilCustomizer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/TamilCustomizer.java @@ -17,7 +17,7 @@ package com.android.inputmethod.keyboard.layout.customizer; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java index b15ef5d26..2232548eb 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java @@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.layout.expected; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; /** * Base class to create an expected keyboard for unit test. diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java index 56149189f..2a040f564 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java @@ -19,12 +19,15 @@ package com.android.inputmethod.keyboard.layout.expected; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.MoreKeySpec; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import java.util.ArrayList; import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * This class builds an actual keyboard for unit test. * @@ -98,9 +101,13 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { @Override public String stringize(final MoreKeySpec spec) { + if (spec == null) { + return "null"; + } return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode); } + @Nonnull static String toString(final String label, final int iconId, final String outputText, final int code) { final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED) @@ -125,7 +132,7 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { static final KeyStringizer STRINGIZER = new KeyStringizer(); @Override - public String stringize(final Key key) { + public String stringize(@Nullable final Key key) { if (key == null) { return "NULL"; } @@ -150,7 +157,8 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { * @param key the key to be converted to string. * @return the human readable representation of <code>key</code>. */ - public static String toString(final Key key) { + @Nonnull + public static String toString(@Nullable final Key key) { return KeyStringizer.STRINGIZER.stringize(key); } @@ -159,7 +167,8 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { * @param keys the keyboard row to be converted to string. * @return the human readable representation of <code>keys</code>. */ - public static String toString(final Key[] keys) { + @Nonnull + public static String toString(@Nullable final Key[] keys) { return KeyStringizer.STRINGIZER.join(keys); } @@ -168,7 +177,7 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer(); @Override - public String stringize(final Key[] keyArray) { + public String stringize(@Nullable final Key[] keyArray) { return KeyStringizer.STRINGIZER.join(keyArray); } } @@ -178,7 +187,8 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { * @param rows the keyboard to be converted to string. * @return the human readable representation of <code>rows</code>. */ - public static String toString(final Key[][] rows) { + @Nonnull + public static String toString(@Nullable final Key[][] rows) { return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */); } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java index 5c1a70fa3..9bb5f187a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java @@ -18,8 +18,8 @@ package com.android.inputmethod.keyboard.layout.expected; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.internal.MoreKeySpec; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/KeyboardLayoutSetSubtypesCountTests.java index 2134eb5fe..6f747b377 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/KeyboardLayoutSetSubtypesCountTests.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/KeyboardLayoutSetSubtypesCountTests.java @@ -27,8 +27,8 @@ import java.util.ArrayList; @SmallTest public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase { - private static final int NUMBER_OF_SUBTYPES = 78; - private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 47; + private static final int NUMBER_OF_SUBTYPES = 85; + private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 50; private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2; @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java index c39a392eb..2d38c874d 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.BengaliAkkhor; import com.android.inputmethod.keyboard.layout.LayoutBase; @@ -29,7 +29,7 @@ import java.util.Locale; /** * bn_BD: Bengali (Bangladesh)/bengali_akkhor */ -@Suppress +@SmallTest public final class TestsBengaliBD extends LayoutTestsBase { private static final Locale LOCALE = new Locale("bn", "BD"); private static final LayoutBase LAYOUT = new BengaliAkkhor(new BengaliBDCustomzier(LOCALE)); @@ -46,6 +46,11 @@ public final class TestsBengaliBD extends LayoutTestsBase { } @Override + public ExpectedKey[] getSpaceKeys(final boolean isPhone) { + return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY)); + } + + @Override public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; } // U+09F3: "৳" BENGALI RUPEE SIGN diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java index a8e872316..613b3bbc2 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Qwerty; @@ -30,7 +30,7 @@ import java.util.Locale; /* * hi_ZZ: Hinglish/qwerty */ -@Suppress +@SmallTest public final class TestsHinglish extends LayoutTestsBase { private static final Locale LOCALE = new Locale("hi", "ZZ"); private static final LayoutBase LAYOUT = new Qwerty(new HinglishCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java index 18baa6152..b581e4a12 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Myanmar; @@ -26,7 +26,7 @@ import java.util.Locale; /** * my_MM: Myanmar (Myanmar)/myanmar */ -@Suppress +@SmallTest public final class TestsMyanmarMM extends LayoutTestsBase { private static final Locale LOCALE = new Locale("my", "MM"); private static final LayoutBase LAYOUT = new Myanmar(LOCALE); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java index ea957e44f..7490d30ab 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.SerbianQwertz; @@ -27,7 +27,7 @@ import java.util.Locale; /** * sr_ZZ: Serbian (Latin)/serbian_qwertz */ -@Suppress +@SmallTest public final class TestsSerbianLatin extends LayoutTestsBase { private static final Locale LOCALE = new Locale("sr", "ZZ"); private static final LayoutBase LAYOUT = new SerbianQwertz(new SerbianLatinCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java index a1984735d..6d9351c9d 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Qwerty; @@ -29,7 +29,7 @@ import java.util.Locale; /** * sr_ZZ: Serbian (Latin)/qwerty */ -@Suppress +@SmallTest public final class TestsSerbianLatinQwerty extends LayoutTestsBase { private static final Locale LOCALE = new Locale("sr", "ZZ"); private static final LayoutBase LAYOUT = new Qwerty(new SerbianLatinQwertyCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java index 72872ba66..6e49c5953 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Symbols; @@ -30,7 +30,7 @@ import java.util.Locale; /** * ta_LK: Tamil (Sri Lanka)/tamil */ -@Suppress +@SmallTest public final class TestsTamilLK extends LayoutTestsBase { private static final Locale LOCALE = new Locale("ta", "LK"); private static final LayoutBase LAYOUT = new Tamil(new TamilLKCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbek.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbek.java index 169de1f31..fd12a6a82 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbek.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbek.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Uzbek; @@ -27,7 +27,7 @@ import java.util.Locale; /** * uz_UZ: Uzbek (Uzbekistan)/uzbek */ -@Suppress +@SmallTest public final class TestsUzbek extends LayoutTestsBase { private static final Locale LOCALE = new Locale("uz", "UZ"); private static final LayoutBase LAYOUT = new Uzbek(new UzbekCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbekQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbekQwerty.java index c210da163..4c33a8cc1 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbekQwerty.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbekQwerty.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.Suppress; +import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Qwerty; @@ -28,7 +28,7 @@ import java.util.Locale; /** * uz_UZ: Uzbek (Uzbekistan)/qwerty */ -@Suppress +@SmallTest public final class TestsUzbekQwerty extends LayoutTestsBase { private static final Locale LOCALE = new Locale("uz", "UZ"); private static final LayoutBase LAYOUT = new Qwerty(new UzbekQwertyCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index fa70f9988..039330c87 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -21,8 +21,8 @@ import android.test.suitebuilder.annotation.LargeTest; import android.util.Pair; import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.common.CodePointUtils; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; -import com.android.inputmethod.latin.makedict.CodePointUtils; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; @@ -32,6 +32,7 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.FileUtils; import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; import java.io.File; import java.io.IOException; @@ -39,7 +40,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; -import java.util.Map; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -49,7 +49,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private static final String TEST_LOCALE = "test"; private static final int DUMMY_PROBABILITY = 0; private static final int[] DICT_FORMAT_VERSIONS = - new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV }; + new int[] { FormatSpec.VERSION402, FormatSpec.VERSION403, FormatSpec.VERSION4_DEV }; private static final String DICTIONARY_ID = "TestDecayingBinaryDictionary"; private int mCurrentTime = 0; @@ -59,6 +59,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { super.setUp(); mCurrentTime = 0; mDictFilesToBeDeleted.clear(); + setCurrentTimeForTestMode(mCurrentTime); } @Override @@ -71,12 +72,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { super.tearDown(); } - private static boolean supportsBeginningOfSentence(final int formatVersion) { - return formatVersion > FormatSpec.VERSION401; + private static boolean supportsCountBasedNgram(final int formatVersion) { + return formatVersion >= FormatSpec.VERSION403; } private static boolean supportsNgram(final int formatVersion) { - return formatVersion >= FormatSpec.VERSION4_DEV; + return formatVersion >= FormatSpec.VERSION403; } private void onInputWord(final BinaryDictionary binaryDictionary, final String word, @@ -136,28 +137,29 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private HashSet<File> mDictFilesToBeDeleted = new HashSet<>(); private File createEmptyDictionaryAndGetFile(final int formatVersion) { - if (formatVersion == FormatSpec.VERSION4 - || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING - || formatVersion == FormatSpec.VERSION4_DEV) { - try { - final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion); - mDictFilesToBeDeleted.add(dictFile); - return dictFile; - } catch (final IOException e) { - fail(e.toString()); - } - } else { - fail("Dictionary format version " + formatVersion + " is not supported."); + return createEmptyDictionaryWithAttributeMapAndGetFile(formatVersion, + new HashMap<String, String>()); + } + + private File createEmptyDictionaryWithAttributeMapAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) { + try { + final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion, + attributeMap); + mDictFilesToBeDeleted.add(dictFile); + return dictFile; + } catch (final IOException e) { + fail(e.toString()); } return null; } - private File createEmptyVer4DictionaryAndGetFile(final int formatVersion) + private File createEmptyVer4DictionaryAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) throws IOException { final File file = File.createTempFile(DICTIONARY_ID, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); FileUtils.deleteRecursively(file); - Map<String, String> attributeMap = new HashMap<>(); attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, DICTIONARY_ID); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); @@ -168,13 +170,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion, LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) { return file; - } else { - throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created. Foramt version: " + formatVersion); } + throw new IOException("Empty dictionary " + file.getAbsolutePath() + + " cannot be created. Foramt version: " + formatVersion); } - private BinaryDictionary getBinaryDictionary(final File dictFile) { + private static BinaryDictionary getBinaryDictionary(final File dictFile) { return new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); @@ -257,12 +258,10 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { onInputWord(binaryDictionary, "a", false /* isValidWord */); assertTrue(binaryDictionary.isValidWord("a")); - onInputWord(binaryDictionary, "b", true /* isValidWord */); - assertTrue(binaryDictionary.isValidWord("b")); - onInputWordWithPrevWord(binaryDictionary, "b", false /* isValidWord */, "a"); assertFalse(isValidBigram(binaryDictionary, "a", "b")); onInputWordWithPrevWord(binaryDictionary, "b", false /* isValidWord */, "a"); + assertTrue(binaryDictionary.isValidWord("b")); assertTrue(isValidBigram(binaryDictionary, "a", "b")); onInputWordWithPrevWord(binaryDictionary, "c", true /* isValidWord */, "a"); @@ -278,16 +277,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { return; } - onInputWordWithPrevWords(binaryDictionary, "c", false /* isValidWord */, "b", "a"); - assertFalse(isValidTrigram(binaryDictionary, "a", "b", "c")); - assertFalse(isValidBigram(binaryDictionary, "b", "c")); - onInputWordWithPrevWords(binaryDictionary, "c", false /* isValidWord */, "b", "a"); + onInputWordWithPrevWords(binaryDictionary, "c", true /* isValidWord */, "b", "a"); assertTrue(isValidTrigram(binaryDictionary, "a", "b", "c")); assertTrue(isValidBigram(binaryDictionary, "b", "c")); - - onInputWordWithPrevWords(binaryDictionary, "d", true /* isValidWord */, "b", "a"); - assertTrue(isValidTrigram(binaryDictionary, "a", "b", "d")); - assertTrue(isValidBigram(binaryDictionary, "b", "d")); + onInputWordWithPrevWords(binaryDictionary, "d", false /* isValidWord */, "c", "b"); + assertFalse(isValidTrigram(binaryDictionary, "b", "c", "d")); + assertFalse(isValidBigram(binaryDictionary, "c", "d")); onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "b", "a"); assertTrue(isValidTrigram(binaryDictionary, "a", "b", "cd")); @@ -306,6 +301,13 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { onInputWord(binaryDictionary, "a", true /* isValidWord */); assertTrue(binaryDictionary.isValidWord("a")); forcePassingShortTime(binaryDictionary); + if (supportsCountBasedNgram(formatVersion)) { + // Count based ngram language model doesn't support decaying based on the elapsed time. + assertTrue(binaryDictionary.isValidWord("a")); + } else { + assertFalse(binaryDictionary.isValidWord("a")); + } + forcePassingLongTime(binaryDictionary); assertFalse(binaryDictionary.isValidWord("a")); onInputWord(binaryDictionary, "a", true /* isValidWord */); @@ -321,6 +323,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { onInputWordWithPrevWord(binaryDictionary, "b", true /* isValidWord */, "a"); assertTrue(isValidBigram(binaryDictionary, "a", "b")); forcePassingShortTime(binaryDictionary); + if (supportsCountBasedNgram(formatVersion)) { + assertTrue(isValidBigram(binaryDictionary, "a", "b")); + } else { + assertFalse(isValidBigram(binaryDictionary, "a", "b")); + } + forcePassingLongTime(binaryDictionary); assertFalse(isValidBigram(binaryDictionary, "a", "b")); onInputWord(binaryDictionary, "a", true /* isValidWord */); @@ -343,7 +351,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { onInputWordWithPrevWord(binaryDictionary, "bc", true /* isValidWord */, "ab"); onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "bc", "ab"); assertTrue(isValidTrigram(binaryDictionary, "ab", "bc", "cd")); - forcePassingShortTime(binaryDictionary); + forcePassingLongTime(binaryDictionary); assertFalse(isValidTrigram(binaryDictionary, "ab", "bc", "cd")); onInputWord(binaryDictionary, "ab", true /* isValidWord */); @@ -388,7 +396,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } final int maxUnigramCount = Integer.parseInt( - binaryDictionary.getPropertyForGettingStats(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); + binaryDictionary.getPropertyForGettingStats( + BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); for (int i = 0; i < unigramTypedCount; i++) { final String word = words.get(random.nextInt(words.size())); onInputWord(binaryDictionary, word, true /* isValidWord */); @@ -476,6 +485,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } private void testAddManyBigramsToDecayingDict(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int unigramCount = 5000; final int bigramCount = 30000; final int bigramTypedCount = 100000; @@ -484,7 +499,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { final Random random = new Random(seed); setCurrentTimeForTestMode(mCurrentTime); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributeMapAndGetFile(formatVersion, + attributeMap); final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); @@ -507,9 +523,6 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { bigrams.add(bigram); } - final int maxBigramCount = Integer.parseInt( - binaryDictionary.getPropertyForGettingStats( - BinaryDictionary.MAX_BIGRAM_COUNT_QUERY)); for (int i = 0; i < bigramTypedCount; ++i) { final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size())); onInputWord(binaryDictionary, bigram.first, true /* isValidWord */); @@ -529,7 +542,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { assertTrue(bigramCountBeforeGC > bigramCountAfterGC); } } - + forcePassingShortTime(binaryDictionary); assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0); assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats( @@ -546,6 +559,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } private void testOverflowBigrams(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int bigramCount = 20000; final int unigramCount = 1000; final int unigramTypedCount = 20; @@ -556,7 +575,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { final long seed = System.currentTimeMillis(); final Random random = new Random(seed); setCurrentTimeForTestMode(mCurrentTime); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributeMapAndGetFile(formatVersion, + attributeMap); final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); @@ -635,27 +655,43 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { assertFalse(binaryDictionary.isValidWord("bbb")); assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb")); + if (supportsNgram(toFormatVersion)) { + onInputWordWithPrevWords(binaryDictionary, "xyz", true, "abc", "aaa"); + assertTrue(isValidTrigram(binaryDictionary, "aaa", "abc", "xyz")); + onInputWordWithPrevWords(binaryDictionary, "def", false, "abc", "aaa"); + assertFalse(isValidTrigram(binaryDictionary, "aaa", "abc", "def")); + } + assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion()); assertTrue(binaryDictionary.migrateTo(toFormatVersion)); assertTrue(binaryDictionary.isValidDictionary()); assertEquals(toFormatVersion, binaryDictionary.getFormatVersion()); assertTrue(binaryDictionary.isValidWord("aaa")); assertFalse(binaryDictionary.isValidWord("bbb")); - assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc")); - onInputWord(binaryDictionary, "bbb", false /* isValidWord */); - assertTrue(binaryDictionary.isValidWord("bbb")); + if (supportsCountBasedNgram(toFormatVersion)) { + assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc")); + onInputWord(binaryDictionary, "bbb", false /* isValidWord */); + assertTrue(binaryDictionary.isValidWord("bbb")); + } assertTrue(isValidBigram(binaryDictionary, "aaa", "abc")); assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb")); - onInputWordWithPrevWord(binaryDictionary, "bbb", false /* isValidWord */, "aaa"); - assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); + if (supportsCountBasedNgram(toFormatVersion)) { + onInputWordWithPrevWord(binaryDictionary, "bbb", false /* isValidWord */, "aaa"); + assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); + } + if (supportsNgram(toFormatVersion)) { + assertTrue(isValidTrigram(binaryDictionary, "aaa", "abc", "xyz")); + assertFalse(isValidTrigram(binaryDictionary, "aaa", "abc", "def")); + onInputWordWithPrevWords(binaryDictionary, "def", false, "abc", "aaa"); + assertTrue(isValidTrigram(binaryDictionary, "aaa", "abc", "def")); + } + binaryDictionary.close(); } public void testBeginningOfSentence() { for (final int formatVersion : DICT_FORMAT_VERSIONS) { - if (supportsBeginningOfSentence(formatVersion)) { - testBeginningOfSentence(formatVersion); - } + testBeginningOfSentence(formatVersion); } } @@ -665,9 +701,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); binaryDictionary.addUnigramEntry("", DUMMY_PROBABILITY, "" /* shortcutTarget */, - BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, - true /* isBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */, - mCurrentTime); + Dictionary.NOT_A_PROBABILITY /* shortcutProbability */, + true /* isBeginningOfSentence */, true /* isNotAWord */, + false /* isPossiblyOffensive */, mCurrentTime); final NgramContext beginningOfSentenceContext = NgramContext.BEGINNING_OF_SENTENCE; onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */); assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); @@ -683,10 +719,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb")); onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */); - assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */); onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */); - assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb")); onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */); assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb")); @@ -713,7 +747,74 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { assertTrue(binaryDictionary.isValidWord("aaa")); assertTrue(binaryDictionary.removeUnigramEntry("aaa")); assertFalse(binaryDictionary.isValidWord("aaa")); - + onInputWord(binaryDictionary, "aaa", false /* isValidWord */); + assertFalse(binaryDictionary.isValidWord("aaa")); + onInputWord(binaryDictionary, "aaa", false /* isValidWord */); + assertTrue(binaryDictionary.isValidWord("aaa")); + assertTrue(binaryDictionary.removeUnigramEntry("aaa")); + assertFalse(binaryDictionary.isValidWord("aaa")); binaryDictionary.close(); } + + public void testUpdateEntriesForInputEvents() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testUpdateEntriesForInputEvents(formatVersion); + } + } + + private void testUpdateEntriesForInputEvents(final int formatVersion) { + setCurrentTimeForTestMode(mCurrentTime); + final int codePointSetSize = 20; + final int EVENT_COUNT = 1000; + final double CONTINUE_RATE = 0.9; + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + final ArrayList<String> unigrams = new ArrayList<>(); + final ArrayList<Pair<String, String>> bigrams = new ArrayList<>(); + final ArrayList<Pair<Pair<String, String>, String>> trigrams = new ArrayList<>(); + + final WordInputEventForPersonalization[] inputEvents = + new WordInputEventForPersonalization[EVENT_COUNT]; + NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; + int prevWordCount = 0; + for (int i = 0; i < inputEvents.length; i++) { + final String word = CodePointUtils.generateWord(random, codePointSet); + inputEvents[i] = new WordInputEventForPersonalization(word, ngramContext, + true /* isValid */, mCurrentTime); + unigrams.add(word); + if (prevWordCount >= 2) { + final Pair<String, String> prevWordsPair = bigrams.get(bigrams.size() - 1); + trigrams.add(new Pair<>(prevWordsPair, word)); + } + if (prevWordCount >= 1) { + bigrams.add(new Pair<>(ngramContext.getNthPrevWord(1 /* n */).toString(), word)); + } + if (random.nextDouble() > CONTINUE_RATE) { + ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; + prevWordCount = 0; + } else { + ngramContext = ngramContext.getNextNgramContext(new WordInfo(word)); + prevWordCount++; + } + } + final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); + binaryDictionary.updateEntriesForInputEvents(inputEvents); + + for (final String word : unigrams) { + assertTrue(binaryDictionary.isInDictionary(word)); + } + for (final Pair<String, String> bigram : bigrams) { + assertTrue(isValidBigram(binaryDictionary, bigram.first, bigram.second)); + } + if (!supportsNgram(formatVersion)) { + return; + } + for (final Pair<Pair<String, String>, String> trigram : trigrams) { + assertTrue(isValidTrigram(binaryDictionary, trigram.first.first, trigram.first.second, + trigram.second)); + } + } } diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index 90dd4366c..fcaa8cdca 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -22,13 +22,14 @@ import android.text.TextUtils; import android.util.Pair; import com.android.inputmethod.latin.NgramContext.WordInfo; -import com.android.inputmethod.latin.makedict.CodePointUtils; +import com.android.inputmethod.latin.common.CodePointUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.WeightedString; import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.FileUtils; -import com.android.inputmethod.latin.utils.LanguageModelParam; import java.io.File; import java.io.IOException; @@ -36,7 +37,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; -import java.util.Map; import java.util.Random; // TODO Use the seed passed as an argument for makedict test. @@ -45,19 +45,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; private static final String TEST_LOCALE = "test"; private static final int[] DICT_FORMAT_VERSIONS = - new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV }; + new int[] { FormatSpec.VERSION402, FormatSpec.VERSION403, FormatSpec.VERSION4_DEV }; private static final String DICTIONARY_ID = "TestBinaryDictionary"; - private static boolean canCheckBigramProbability(final int formatVersion) { - return formatVersion > FormatSpec.VERSION401; - } - - private static boolean supportsBeginningOfSentence(final int formatVersion) { - return formatVersion > FormatSpec.VERSION401; - } - private static boolean supportsNgram(final int formatVersion) { - return formatVersion >= FormatSpec.VERSION4_DEV; + return formatVersion >= FormatSpec.VERSION403; } private HashSet<File> mDictFilesToBeDeleted = new HashSet<>(); @@ -78,38 +70,38 @@ public class BinaryDictionaryTests extends AndroidTestCase { } private File createEmptyDictionaryAndGetFile(final int formatVersion) { - if (formatVersion == FormatSpec.VERSION4 - || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING - || formatVersion == FormatSpec.VERSION4_DEV) { - try { - final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion); - mDictFilesToBeDeleted.add(dictFile); - return dictFile; - } catch (final IOException e) { - fail(e.toString()); - } - } else { - fail("Dictionary format version " + formatVersion + " is not supported."); + return createEmptyDictionaryWithAttributesAndGetFile(formatVersion, + new HashMap<String, String>()); + } + + private File createEmptyDictionaryWithAttributesAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) { + try { + final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion, + attributeMap); + mDictFilesToBeDeleted.add(dictFile); + return dictFile; + } catch (final IOException e) { + fail(e.toString()); } return null; } - private File createEmptyVer4DictionaryAndGetFile(final int formatVersion) throws IOException { + private File createEmptyVer4DictionaryAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) throws IOException { final File file = File.createTempFile(DICTIONARY_ID, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); file.delete(); file.mkdir(); - Map<String, String> attributeMap = new HashMap<>(); if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion, Locale.ENGLISH, attributeMap)) { return file; - } else { - throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created. Format version: " + formatVersion); } + throw new IOException("Empty dictionary " + file.getAbsolutePath() + + " cannot be created. Format version: " + formatVersion); } - private BinaryDictionary getBinaryDictionary(final File dictFile) { + private static BinaryDictionary getBinaryDictionary(final File dictFile) { return new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); @@ -192,7 +184,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { // Too long short cut. binaryDictionary.addUnigramEntry("a", probability, invalidLongWord, 10 /* shortcutProbability */, false /* isBeginningOfSentence */, - false /* isNotAWord */, false /* isBlacklisted */, + false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); addUnigramWord(binaryDictionary, "abc", probability); final int updatedProbability = 200; @@ -203,17 +195,17 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(probability, binaryDictionary.getFrequency("aaa")); assertEquals(updatedProbability, binaryDictionary.getFrequency(validLongWord)); - assertEquals(BinaryDictionary.NOT_A_PROBABILITY, - binaryDictionary.getFrequency(invalidLongWord)); + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency(invalidLongWord)); assertEquals(updatedProbability, binaryDictionary.getFrequency("abc")); } private static void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, final int probability) { binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */, - BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, + Dictionary.NOT_A_PROBABILITY /* shortcutProbability */, false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); } private static void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, @@ -344,18 +336,14 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertTrue(isValidBigram(binaryDictionary, "aaa", "bcc")); assertTrue(isValidBigram(binaryDictionary, "abb", "aaa")); assertTrue(isValidBigram(binaryDictionary, "abb", "bcc")); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); - } + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); addBigramWords(binaryDictionary, "aaa", "abb", updatedBigramProbability); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(updatedBigramProbability, - getBigramProbability(binaryDictionary, "aaa", "abb")); - } + assertEquals(updatedBigramProbability, + getBigramProbability(binaryDictionary, "aaa", "abb")); assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa")); assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc")); @@ -375,17 +363,12 @@ public class BinaryDictionaryTests extends AndroidTestCase { addUnigramWord(binaryDictionary, "abc", unigramProbability); addUnigramWord(binaryDictionary, "f", unigramProbability); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, "abcde", "fghij")); - } + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abcde", "fghij")); assertEquals(Dictionary.NOT_A_PROBABILITY, getBigramProbability(binaryDictionary, "abcde", "fgh")); addBigramWords(binaryDictionary, "abcde", "fghij", updatedBigramProbability); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(updatedBigramProbability, - getBigramProbability(binaryDictionary, "abcde", "fghij")); - } + assertEquals(updatedBigramProbability, + getBigramProbability(binaryDictionary, "abcde", "fghij")); } public void testRandomlyAddBigramWords() { @@ -435,10 +418,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int bigramProbability = bigramProbabilities.get(bigram); assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, isValidBigram(binaryDictionary, bigram.first, bigram.second)); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); } } @@ -588,12 +569,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); assertEquals(unigramProbability, binaryDictionary.getFrequency("abb")); assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc")); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); - } + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa")); assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc")); assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa")); @@ -655,10 +634,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int bigramProbability = bigramProbabilities.get(bigram); assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, isValidBigram(binaryDictionary, bigram.first, bigram.second)); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); } } @@ -669,6 +646,12 @@ public class BinaryDictionaryTests extends AndroidTestCase { } private void testRandomOperationsAndFlashWithGC(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int flashWithGCIterationCount = 50; final int operationCountInEachIteration = 200; final int initialUnigramCount = 100; @@ -679,7 +662,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributesAndGetFile(formatVersion, + attributeMap); BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final ArrayList<String> words = new ArrayList<>(); @@ -755,10 +739,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { probability = Dictionary.NOT_A_PROBABILITY; } - if (canCheckBigramProbability(formatVersion)) { - assertEquals(probability, - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } + assertEquals(probability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); assertEquals(probability != Dictionary.NOT_A_PROBABILITY, isValidBigram(binaryDictionary, bigram.first, bigram.second)); } @@ -815,13 +797,20 @@ public class BinaryDictionaryTests extends AndroidTestCase { } private void testUnigramAndBigramCount(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int flashWithGCIterationCount = 10; final int codePointSetSize = 50; final int unigramCountPerIteration = 1000; final int bigramCountPerIteration = 2000; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributesAndGetFile(formatVersion, + attributeMap); final ArrayList<String> words = new ArrayList<>(); final HashSet<Pair<String, String>> bigrams = new HashSet<>(); @@ -863,63 +852,6 @@ public class BinaryDictionaryTests extends AndroidTestCase { } } - public void testAddMultipleDictionaryEntries() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddMultipleDictionaryEntries(formatVersion); - } - } - - private void testAddMultipleDictionaryEntries(final int formatVersion) { - final int codePointSetSize = 20; - final int lmParamCount = 1000; - final double bigramContinueRate = 0.9; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); - - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); - - final LanguageModelParam[] languageModelParams = new LanguageModelParam[lmParamCount]; - String prevWord = null; - for (int i = 0; i < languageModelParams.length; i++) { - final String word = CodePointUtils.generateWord(random, codePointSet); - final int probability = random.nextInt(0xFF); - final int bigramProbability = probability + random.nextInt(0xFF - probability); - unigramProbabilities.put(word, probability); - if (prevWord == null) { - languageModelParams[i] = new LanguageModelParam(word, probability, - BinaryDictionary.NOT_A_VALID_TIMESTAMP); - } else { - languageModelParams[i] = new LanguageModelParam(prevWord, word, probability, - bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP); - bigramProbabilities.put(new Pair<>(prevWord, word), - bigramProbability); - } - prevWord = (random.nextDouble() < bigramContinueRate) ? word : null; - } - - final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); - binaryDictionary.addMultipleDictionaryEntries(languageModelParams); - - for (Map.Entry<String, Integer> entry : unigramProbabilities.entrySet()) { - assertEquals((int)entry.getValue(), binaryDictionary.getFrequency(entry.getKey())); - } - - for (Map.Entry<Pair<String, String>, Integer> entry : bigramProbabilities.entrySet()) { - final String word0 = entry.getKey().first; - final String word1 = entry.getKey().second; - final int bigramProbability = entry.getValue(); - assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, - isValidBigram(binaryDictionary, word0, word1)); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, word0, word1)); - } - } - } - public void testGetWordProperties() { for (final int formatVersion : DICT_FORMAT_VERSIONS) { testGetWordProperties(formatVersion); @@ -949,11 +881,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { final String word = CodePointUtils.generateWord(random, codePointSet); final int unigramProbability = random.nextInt(0xFF); final boolean isNotAWord = random.nextBoolean(); - final boolean isBlacklisted = random.nextBoolean(); + final boolean isPossiblyOffensive = random.nextBoolean(); // TODO: Add tests for historical info. binaryDictionary.addUnigramEntry(word, unigramProbability, - null /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY, - false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, + null /* shortcutTarget */, Dictionary.NOT_A_PROBABILITY, + false /* isBeginningOfSentence */, isNotAWord, isPossiblyOffensive, BinaryDictionary.NOT_A_VALID_TIMESTAMP); if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { binaryDictionary.flushWithGC(); @@ -965,7 +897,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(word, wordProperty.mWord); assertTrue(wordProperty.isValid()); assertEquals(isNotAWord, wordProperty.mIsNotAWord); - assertEquals(isBlacklisted, wordProperty.mIsBlacklistEntry); + assertEquals(isPossiblyOffensive, wordProperty.mIsPossiblyOffensive); assertEquals(false, wordProperty.mHasNgrams); assertEquals(false, wordProperty.mHasShortcuts); assertEquals(unigramProbability, wordProperty.mProbabilityInfo.mProbability); @@ -1008,10 +940,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { for (final WeightedString bigramTarget : wordProperty.getBigrams()) { final String word1 = bigramTarget.mWord; assertTrue(bigramWord1s.contains(word1)); - if (canCheckBigramProbability(formatVersion)) { - final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1)); - assertEquals(bigramProbability, bigramTarget.getProbability()); - } + final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1)); + assertEquals(bigramProbability, bigramTarget.getProbability()); } } } @@ -1094,10 +1024,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { final String word1 = bigramTarget.mWord; assertTrue(bigramWord1s.contains(word1)); final Pair<String, String> bigram = new Pair<>(word0, word1); - if (canCheckBigramProbability(formatVersion)) { - final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram); - assertEquals(bigramProbability, bigramTarget.getProbability()); - } + final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram); + assertEquals(bigramProbability, bigramTarget.getProbability()); bigramSet.remove(bigram); } } @@ -1120,7 +1048,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int shortcutProbability = 10; binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz", shortcutProbability, false /* isBeginningOfSentence */, - false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */); + false /* isNotAWord */, false /* isPossiblyOffensive */, 0 /* timestamp */); WordProperty wordProperty = binaryDictionary.getWordProperty("aaa", false /* isBeginningOfSentence */); assertEquals(1, wordProperty.mShortcutTargets.size()); @@ -1129,7 +1057,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int updatedShortcutProbability = 2; binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz", updatedShortcutProbability, false /* isBeginningOfSentence */, - false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */); + false /* isNotAWord */, false /* isPossiblyOffensive */, 0 /* timestamp */); wordProperty = binaryDictionary.getWordProperty("aaa", false /* isBeginningOfSentence */); assertEquals(1, wordProperty.mShortcutTargets.size()); @@ -1138,7 +1066,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { wordProperty.mShortcutTargets.get(0).getProbability()); binaryDictionary.addUnigramEntry("aaa", unigramProbability, "yyy", shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, 0 /* timestamp */); + false /* isPossiblyOffensive */, 0 /* timestamp */); final HashMap<String, Integer> shortcutTargets = new HashMap<>(); shortcutTargets.put("zzz", updatedShortcutProbability); shortcutTargets.put("yyy", shortcutProbability); @@ -1201,7 +1129,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int unigramProbability = unigramProbabilities.get(word); binaryDictionary.addUnigramEntry(word, unigramProbability, shortcutTarget, shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, 0 /* timestamp */); + false /* isPossiblyOffensive */, 0 /* timestamp */); if (shortcutTargets.containsKey(word)) { final HashMap<String, Integer> shortcutTargetsOfWord = shortcutTargets.get(word); shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability); @@ -1233,6 +1161,15 @@ public class BinaryDictionaryTests extends AndroidTestCase { } } + public void testPossiblyOffensiveAttributeMaintained() { + final BinaryDictionary binaryDictionary = + getEmptyBinaryDictionary(FormatSpec.VERSION403); + binaryDictionary.addUnigramEntry("ddd", 100, null, Dictionary.NOT_A_PROBABILITY, + false, true, true, 0); + WordProperty wordProperty = binaryDictionary.getWordProperty("ddd", false); + assertEquals(true, wordProperty.mIsPossiblyOffensive); + } + public void testDictMigration() { for (final int formatVersion : DICT_FORMAT_VERSIONS) { testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); @@ -1249,10 +1186,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int shortcutProbability = 10; binaryDictionary.addUnigramEntry("ccc", unigramProbability, "xxx", shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, 0 /* timestamp */); + false /* isPossiblyOffensive */, 0 /* timestamp */); binaryDictionary.addUnigramEntry("ddd", unigramProbability, null /* shortcutTarget */, Dictionary.NOT_A_PROBABILITY, false /* isBeginningOfSentence */, - true /* isNotAWord */, true /* isBlacklisted */, 0 /* timestamp */); + true /* isNotAWord */, true /* isPossiblyOffensive */, 0 /* timestamp */); binaryDictionary.addNgramEntry(NgramContext.BEGINNING_OF_SENTENCE, "aaa", bigramProbability, 0 /* timestamp */); assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); @@ -1264,11 +1201,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(toFormatVersion, binaryDictionary.getFormatVersion()); assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb")); - if (canCheckBigramProbability(toFormatVersion)) { - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb")); - assertEquals(bigramProbability, binaryDictionary.getNgramProbability( - NgramContext.BEGINNING_OF_SENTENCE, "aaa")); - } + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb")); + assertEquals(bigramProbability, binaryDictionary.getNgramProbability( + NgramContext.BEGINNING_OF_SENTENCE, "aaa")); assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); WordProperty wordProperty = binaryDictionary.getWordProperty("ccc", false /* isBeginningOfSentence */); @@ -1276,7 +1211,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals("xxx", wordProperty.mShortcutTargets.get(0).mWord); wordProperty = binaryDictionary.getWordProperty("ddd", false /* isBeginningOfSentence */); - assertTrue(wordProperty.mIsBlacklistEntry); + assertTrue(wordProperty.mIsPossiblyOffensive); assertTrue(wordProperty.mIsNotAWord); } @@ -1339,10 +1274,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.getPropertyForGettingStats(BinaryDictionary.UNIGRAM_COUNT_QUERY))); for (final Pair<String, String> bigram : bigrams) { - if (canCheckBigramProbability(toFormatVersion)) { - assertEquals((int)bigramProbabilities.get(bigram), - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } + assertEquals((int)bigramProbabilities.get(bigram), + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); assertTrue(isValidBigram(binaryDictionary, bigram.first, bigram.second)); } assertEquals(bigramProbabilities.size(), Integer.parseInt( @@ -1351,9 +1284,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { public void testBeginningOfSentence() { for (final int formatVersion : DICT_FORMAT_VERSIONS) { - if (supportsBeginningOfSentence(formatVersion)) { - testBeginningOfSentence(formatVersion); - } + testBeginningOfSentence(formatVersion); } } diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java index ae5cc5c73..1c8a2f242 100644 --- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java +++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java @@ -20,6 +20,8 @@ import android.test.suitebuilder.annotation.LargeTest; import android.text.style.SuggestionSpan; import android.text.style.UnderlineSpan; +import com.android.inputmethod.latin.common.Constants; + @LargeTest public class BlueUnderlineTests extends InputTestsBase { @@ -61,7 +63,6 @@ public class BlueUnderlineTests extends InputTestsBase { public void testBlueUnderlineOnBackspace() { final String STRING_TO_TYPE = "tgis"; final int typedLength = STRING_TO_TYPE.length(); - final int EXPECTED_SUGGESTION_SPAN_START = -1; final int EXPECTED_UNDERLINE_SPAN_START = 0; final int EXPECTED_UNDERLINE_SPAN_END = 3; type(STRING_TO_TYPE); diff --git a/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java b/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java index ed3929dc7..3ad659a99 100644 --- a/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java +++ b/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java @@ -47,7 +47,7 @@ public class DictionaryFacilitatorLruCacheTests extends AndroidTestCase { getContext(), MAX_CACHE_SIZE_LARGE, "")); } - private void testGetFacilitator(final DictionaryFacilitatorLruCache cache) { + private static void testGetFacilitator(final DictionaryFacilitatorLruCache cache) { final DictionaryFacilitator dictionaryFacilitatorEnUs = cache.get(Locale.US); assertNotNull(dictionaryFacilitatorEnUs); assertTrue(dictionaryFacilitatorEnUs.isForLocales(new Locale[] { Locale.US })); @@ -68,7 +68,7 @@ public class DictionaryFacilitatorLruCacheTests extends AndroidTestCase { getContext(), MAX_CACHE_SIZE_LARGE, "")); } - private void testSetUseContactsDictionary(final DictionaryFacilitatorLruCache cache) { + private static void testSetUseContactsDictionary(final DictionaryFacilitatorLruCache cache) { assertNull(cache.get(Locale.US).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); cache.setUseContactsDictionary(true /* useContactsDictionary */); assertNotNull(cache.get(Locale.US).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java index 09309bcc0..07d7c3225 100644 --- a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java @@ -35,16 +35,20 @@ public class FusionDictionaryTests extends AndroidTestCase { FusionDictionary dict = new FusionDictionary(new PtNodeArray(), new DictionaryOptions(new HashMap<String,String>())); - dict.add("abc", new ProbabilityInfo(10), null, false /* isNotAWord */); + dict.add("abc", new ProbabilityInfo(10), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa")); assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "abc")); - dict.add("aa", new ProbabilityInfo(10), null, false /* isNotAWord */); + dict.add("aa", new ProbabilityInfo(10), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa")); assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aa")); - dict.add("babcd", new ProbabilityInfo(10), null, false /* isNotAWord */); - dict.add("bacde", new ProbabilityInfo(10), null, false /* isNotAWord */); + dict.add("babcd", new ProbabilityInfo(10), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("bacde", new ProbabilityInfo(10), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "ba")); assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "babcd")); assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "bacde")); diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index 99dc9a204..c76f6f446 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -21,6 +21,7 @@ import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.inputmethod.BaseInputConnection; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.Settings; @LargeTest @@ -37,7 +38,7 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = "thi"; type(WORD_TO_TYPE); pickSuggestionManually(WORD_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("press suggestion then backspace", EXPECTED_RESULT, mEditText.getText().toString()); @@ -50,7 +51,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the auto-correction. For "tgis", the auto-correction should be "this". pickSuggestionManually(WORD_TO_PICK); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); assertEquals("pick typed word over auto-correction then backspace", WORD_TO_PICK, mEditText.getText().toString()); type(Constants.CODE_DELETE); @@ -64,7 +65,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the typed word. pickSuggestionManually(WORD_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); assertEquals("pick typed word over auto-correction then backspace", WORD_TO_TYPE, mEditText.getText().toString()); type(Constants.CODE_DELETE); @@ -79,7 +80,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the second suggestion, which should be "thus" when "tgis" is typed. pickSuggestionManually(WORD_TO_PICK); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); assertEquals("pick different suggestion then backspace", WORD_TO_PICK, mEditText.getText().toString()); type(Constants.CODE_DELETE); @@ -94,7 +95,8 @@ public class InputLogicTests extends InputTestsBase { final int SELECTION_END = 19; final String EXPECTED_RESULT = "some text some text"; type(STRING_TO_TYPE); - // There is no IMF to call onUpdateSelection for us so we must do it by hand. + // Don't use the sendUpdateForCursorMove* family of methods here because they + // don't handle selections. // Send once to simulate the cursor actually responding to the move caused by typing. // This is necessary because LatinIME is bookkeeping to avoid confusing a real cursor // move with a move triggered by LatinIME inputting stuff. @@ -114,7 +116,8 @@ public class InputLogicTests extends InputTestsBase { final int SELECTION_END = 19; final String EXPECTED_RESULT = "some text some text"; type(STRING_TO_TYPE); - // There is no IMF to call onUpdateSelection for us so we must do it by hand. + // Don't use the sendUpdateForCursorMove* family of methods here because they + // don't handle selections. // Send once to simulate the cursor actually responding to the move caused by typing. // This is necessary because LatinIME is bookkeeping to avoid confusing a real cursor // move with a move triggered by LatinIME inputting stuff. @@ -153,7 +156,7 @@ public class InputLogicTests extends InputTestsBase { final String STRING_TO_TYPE = "tgis."; final String EXPECTED_RESULT = "tgis."; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("auto-correct with period then revert", EXPECTED_RESULT, mEditText.getText().toString()); @@ -166,7 +169,7 @@ public class InputLogicTests extends InputTestsBase { final String STRING_TO_TYPE = "tgis "; final String EXPECTED_RESULT = "tgis"; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("auto-correct with space then revert", EXPECTED_RESULT, mEditText.getText().toString()); @@ -177,15 +180,12 @@ public class InputLogicTests extends InputTestsBase { final String STRING_TO_TYPE_SECOND = "a"; final String EXPECTED_RESULT = "tgis a"; type(STRING_TO_TYPE_FIRST); - mLatinIME.onUpdateSelection(0, 0, - STRING_TO_TYPE_FIRST.length(), STRING_TO_TYPE_FIRST.length(), -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE_FIRST.length()); type(Constants.CODE_DELETE); type(STRING_TO_TYPE_SECOND); - mLatinIME.onUpdateSelection(STRING_TO_TYPE_FIRST.length(), STRING_TO_TYPE_FIRST.length(), - STRING_TO_TYPE_FIRST.length() - 1 + STRING_TO_TYPE_SECOND.length(), - STRING_TO_TYPE_FIRST.length() - 1 + STRING_TO_TYPE_SECOND.length(), - -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE_FIRST.length() - 1 + + STRING_TO_TYPE_SECOND.length()); assertEquals("auto-correct with space then revert then type more", EXPECTED_RESULT, mEditText.getText().toString()); } @@ -194,7 +194,7 @@ public class InputLogicTests extends InputTestsBase { final String STRING_TO_TYPE = "this "; final String EXPECTED_RESULT = "this"; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("auto-correct with space does not revert", EXPECTED_RESULT, mEditText.getText().toString()); @@ -298,10 +298,9 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = "this "; final int NEW_CURSOR_POSITION = 0; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); + sendUpdateForCursorMoveTo(typedLength); mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION); - mLatinIME.onUpdateSelection(typedLength, typedLength, - NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1); + sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION); type(Constants.CODE_DELETE); assertEquals("auto correct then move cursor to start of line then backspace", EXPECTED_RESULT, mEditText.getText().toString()); @@ -313,10 +312,9 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = "andthis "; final int NEW_CURSOR_POSITION = STRING_TO_TYPE.indexOf('t'); type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); + sendUpdateForCursorMoveTo(typedLength); mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION); - mLatinIME.onUpdateSelection(typedLength, typedLength, - NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1); + sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION); type(Constants.CODE_DELETE); assertEquals("auto correct then move cursor then backspace", EXPECTED_RESULT, mEditText.getText().toString()); @@ -416,7 +414,7 @@ public class InputLogicTests extends InputTestsBase { BaseInputConnection.getComposingSpanStart(mEditText.getText())); assertEquals("resume suggestion on backspace", -1, BaseInputConnection.getComposingSpanEnd(mEditText.getText())); - mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); + sendUpdateForCursorMoveTo(typedLength); type(Constants.CODE_DELETE); assertEquals("resume suggestion on backspace", 4, BaseInputConnection.getComposingSpanStart(mEditText.getText())); @@ -557,26 +555,22 @@ public class InputLogicTests extends InputTestsBase { final int endOfSuggestion = endOfPrefix + FIRST_NON_TYPED_SUGGESTION.length(); final int indexForManualCursor = endOfPrefix + 3; // +3 because it's after "Bar" in "Barack" type(PREFIX); - mLatinIME.onUpdateSelection(0, 0, endOfPrefix, endOfPrefix, -1, -1); + sendUpdateForCursorMoveTo(endOfPrefix); type(WORD_TO_TYPE); pickSuggestionManually(FIRST_NON_TYPED_SUGGESTION); - mLatinIME.onUpdateSelection(endOfPrefix, endOfPrefix, endOfSuggestion, endOfSuggestion, - -1, -1); + sendUpdateForCursorMoveTo(endOfSuggestion); runMessages(); type(" "); - mLatinIME.onUpdateSelection(endOfSuggestion, endOfSuggestion, - endOfSuggestion + 1, endOfSuggestion + 1, -1, -1); + sendUpdateForCursorMoveBy(1); sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Simulate a manual cursor move mInputConnection.setSelection(indexForManualCursor, indexForManualCursor); - mLatinIME.onUpdateSelection(endOfSuggestion + 1, endOfSuggestion + 1, - indexForManualCursor, indexForManualCursor, -1, -1); + sendUpdateForCursorMoveTo(indexForManualCursor); sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); pickSuggestionManually(WORD_TO_TYPE); - mLatinIME.onUpdateSelection(indexForManualCursor, indexForManualCursor, - endOfWord, endOfWord, -1, -1); + sendUpdateForCursorMoveTo(endOfWord); sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed @@ -679,4 +673,68 @@ public class InputLogicTests extends InputTestsBase { MoreAsserts.assertNotEqual("gesture twice the same thing", "this", mEditText.getText().toString()); } + + private void typeOrGestureWordAndPutCursorInside(final boolean gesture, final String word, + final int startPos) { + final int END_OF_WORD = startPos + word.length(); + final int NEW_CURSOR_POSITION = startPos + word.length() / 2; + if (gesture) { + gesture(word); + } else { + type(word); + } + sendUpdateForCursorMoveTo(END_OF_WORD); + runMessages(); + sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); + runMessages(); + ensureComposingSpanPos("move cursor inside word leaves composing span in the right place", + startPos, END_OF_WORD); + } + + private void typeWordAndPutCursorInside(final String word, final int startPos) { + typeOrGestureWordAndPutCursorInside(false /* gesture */, word, startPos); + } + + private void gestureWordAndPutCursorInside(final String word, final int startPos) { + typeOrGestureWordAndPutCursorInside(true /* gesture */, word, startPos); + } + + private void ensureComposingSpanPos(final String message, final int from, final int to) { + assertEquals(message, from, BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals(message, to, BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + } + + public void testTypeWithinComposing() { + final String WORD_TO_TYPE = "something"; + final String EXPECTED_RESULT = "some thing"; + typeWordAndPutCursorInside(WORD_TO_TYPE, 0 /* startPos */); + type(" "); + ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1); + assertEquals("space in the middle of a composing word", EXPECTED_RESULT, + mEditText.getText().toString()); + int cursorPos = sendUpdateForCursorMoveToEndOfLine(); + runMessages(); + type(" "); + assertEquals("mbo", "some thing ", mEditText.getText().toString()); + typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */); + type(Constants.CODE_DELETE); + ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1); + } + + public void testTypeWithinGestureComposing() { + final String WORD_TO_TYPE = "something"; + final String EXPECTED_RESULT = "some thing"; + gestureWordAndPutCursorInside(WORD_TO_TYPE, 0 /* startPos */); + type(" "); + ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1); + assertEquals("space in the middle of a composing word", EXPECTED_RESULT, + mEditText.getText().toString()); + int cursorPos = sendUpdateForCursorMoveToEndOfLine(); + runMessages(); + type(" "); + typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */); + type(Constants.CODE_DELETE); + ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1); + } } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.java index afe7dbe70..4b44138a7 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.java @@ -19,13 +19,14 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; import com.android.inputmethod.event.Event; +import com.android.inputmethod.latin.common.Constants; import java.util.ArrayList; @LargeTest public class InputLogicTestsDeadKeys extends InputTestsBase { // A helper class for readability - private static class EventList extends ArrayList<Event> { + static class EventList extends ArrayList<Event> { public EventList addCodePoint(final int codePoint, final boolean isDead) { final Event event; if (isDead) { diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java index c16372ab5..6e6f551cc 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java @@ -19,6 +19,8 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; import android.view.inputmethod.BaseInputConnection; +import com.android.inputmethod.latin.common.Constants; + @LargeTest public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { public void testAutoCorrectForLanguageWithoutSpaces() { diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java index 842b54fe1..3cfd0e2a6 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.settings.Settings; @LargeTest diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java index ab69c8592..1372514da 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java @@ -80,7 +80,6 @@ import android.util.Pair; @LargeTest // These tests are inactive until the combining code for Myanmar Reordering is sorted out. @Suppress -@SuppressWarnings("rawtypes") public class InputLogicTestsReorderingMyanmar extends InputTestsBase { // The tests are formatted as follows. // Each test is an entry in the array of Pair arrays. @@ -90,7 +89,7 @@ public class InputLogicTestsReorderingMyanmar extends InputTestsBase { // member is stored the string that should be in the text view after this // key press. - private static final Pair[][] TESTS = { + private static final Pair<?, ?>[][] TESTS = { // Tests for U+1031 MYANMAR VOWEL SIGN E : ေ new Pair[] { // Type : U+1031 U+1000 U+101F ေ က ဟ @@ -206,13 +205,12 @@ public class InputLogicTestsReorderingMyanmar extends InputTestsBase { */ }; - @SuppressWarnings("unchecked") - private void doMyanmarTest(final int testNumber, final Pair[] test) { + private void doMyanmarTest(final int testNumber, final Pair<?, ?>[] test) { int stepNumber = 0; - for (final Pair<int[], String> step : test) { + for (final Pair<?, ?> step : test) { ++stepNumber; - final int[] input = step.first; - final String expectedResult = step.second; + final int[] input = (int[]) step.first; + final String expectedResult = (String) step.second; if (input.length > 1) { mLatinIME.onTextInput(new String(input, 0, input.length)); } else { @@ -226,7 +224,7 @@ public class InputLogicTestsReorderingMyanmar extends InputTestsBase { public void testMyanmarReordering() { int testNumber = 0; changeLanguage("my_MM", "CombiningRules=MyanmarReordering"); - for (final Pair[] test : TESTS) { + for (final Pair<?, ?>[] test : TESTS) { // Small trick to reset LatinIME : setText("") and send updateSelection with values // LatinIME has never seen, and cursor pos 0,0. mEditText.setText(""); diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index dd900a22c..926a2d3e1 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -40,13 +40,14 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.event.Event; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.Dictionary.PhonyDictionary; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.LocaleUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Locale; @@ -193,6 +194,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { | InputType.TYPE_TEXT_FLAG_MULTI_LINE; mEditText.setInputType(inputType); mEditText.setEnabled(true); + mLastCursorPos = 0; if (null == Looper.myLooper()) { Looper.prepare(); } @@ -250,7 +252,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // Now, Looper#loop() never exits in normal operation unless the Looper#quit() method // is called, which has a lot of bad side effects. We can however just throw an exception // in the runnable which will unwind the stack and allow us to exit. - private final class InterruptRunMessagesException extends RuntimeException { + final class InterruptRunMessagesException extends RuntimeException { // Empty class } protected void runMessages() { @@ -284,7 +286,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { } else { final int x = key.getX() + key.getWidth() / 2; final int y = key.getY() + key.getHeight() / 2; - event = mLatinIME.createSoftwareKeypressEvent(codePoint, x, y, isKeyRepeat); + event = LatinIME.createSoftwareKeypressEvent(codePoint, x, y, isKeyRepeat); } mLatinIME.onEvent(event); // Also see the comment at the top of this function about onReleaseKey @@ -309,9 +311,8 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { final Key key = mKeyboard.getKey(codePoint); if (key == null) { throw new RuntimeException("Code point not on the keyboard"); - } else { - return new Point(key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2); } + return new Point(key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2); } protected void gesture(final String stringToGesture) { @@ -386,7 +387,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, 0 /* id */); - SubtypeSwitcher.getInstance().forceSubtype(subtype); + SubtypeSwitcher.forceSubtype(subtype); mLatinIME.onCurrentInputMethodSubtypeChanged(subtype); runMessages(); mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); @@ -415,4 +416,40 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { Thread.sleep(milliseconds); } catch (InterruptedException e) {} } + + // Some helper methods to manage the mock cursor position + // DO NOT CALL LatinIME#onUpdateSelection IF YOU WANT TO USE THOSE + int mLastCursorPos = 0; + /** + * Move the cached cursor position to the passed position and send onUpdateSelection to LatinIME + */ + protected int sendUpdateForCursorMoveTo(final int position) { + mInputConnection.setSelection(position, position); + mLatinIME.onUpdateSelection(mLastCursorPos, mLastCursorPos, position, position, -1, -1); + mLastCursorPos = position; + return position; + } + + /** + * Move the cached cursor position by the passed amount and send onUpdateSelection to LatinIME + */ + protected int sendUpdateForCursorMoveBy(final int offset) { + final int lastPos = mEditText.getText().length(); + final int requestedPosition = mLastCursorPos + offset; + if (requestedPosition < 0) { + return sendUpdateForCursorMoveTo(0); + } else if (requestedPosition > lastPos) { + return sendUpdateForCursorMoveTo(lastPos); + } else { + return sendUpdateForCursorMoveTo(requestedPosition); + } + } + + /** + * Move the cached cursor position to the end of the line and send onUpdateSelection to LatinIME + */ + protected int sendUpdateForCursorMoveToEndOfLine() { + final int lastPos = mEditText.getText().length(); + return sendUpdateForCursorMoveTo(lastPos); + } } diff --git a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java index f5e993de8..22114b7a0 100644 --- a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java +++ b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; -import com.android.inputmethod.latin.makedict.CodePointUtils; +import com.android.inputmethod.latin.common.CodePointUtils; import java.util.Random; diff --git a/tests/src/com/android/inputmethod/latin/NgramContextTests.java b/tests/src/com/android/inputmethod/latin/NgramContextTests.java index ecc2c634d..ab1819d0b 100644 --- a/tests/src/com/android/inputmethod/latin/NgramContextTests.java +++ b/tests/src/com/android/inputmethod/latin/NgramContextTests.java @@ -25,8 +25,8 @@ import android.test.suitebuilder.annotation.SmallTest; public class NgramContextTests extends AndroidTestCase { public void testConstruct() { assertEquals(new NgramContext(new WordInfo("a")), new NgramContext(new WordInfo("a"))); - assertEquals(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE), - new NgramContext(WordInfo.BEGINNING_OF_SENTENCE)); + assertEquals(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO), + new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO)); assertEquals(new NgramContext(WordInfo.EMPTY_WORD_INFO), new NgramContext(WordInfo.EMPTY_WORD_INFO)); assertEquals(new NgramContext(WordInfo.EMPTY_WORD_INFO), @@ -35,17 +35,18 @@ public class NgramContextTests extends AndroidTestCase { public void testIsBeginningOfSentenceContext() { assertFalse(new NgramContext().isBeginningOfSentenceContext()); - assertTrue(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE) + assertTrue(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO) .isBeginningOfSentenceContext()); assertTrue(NgramContext.BEGINNING_OF_SENTENCE.isBeginningOfSentenceContext()); assertFalse(new NgramContext(new WordInfo("a")).isBeginningOfSentenceContext()); assertFalse(new NgramContext(new WordInfo("")).isBeginningOfSentenceContext()); assertFalse(new NgramContext(WordInfo.EMPTY_WORD_INFO).isBeginningOfSentenceContext()); - assertTrue(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE, new WordInfo("a")) + assertTrue(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO, new WordInfo("a")) .isBeginningOfSentenceContext()); - assertFalse(new NgramContext(new WordInfo("a"), WordInfo.BEGINNING_OF_SENTENCE) + assertFalse(new NgramContext(new WordInfo("a"), WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO) .isBeginningOfSentenceContext()); - assertFalse(new NgramContext(WordInfo.EMPTY_WORD_INFO, WordInfo.BEGINNING_OF_SENTENCE) + assertFalse(new NgramContext( + WordInfo.EMPTY_WORD_INFO, WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO) .isBeginningOfSentenceContext()); } @@ -56,7 +57,7 @@ public class NgramContextTests extends AndroidTestCase { assertEquals("b", ngramContext_b_a.getNthPrevWord(1)); assertEquals("a", ngramContext_b_a.getNthPrevWord(2)); final NgramContext ngramContext_bos_b = - ngramContext_b_a.getNextNgramContext(WordInfo.BEGINNING_OF_SENTENCE); + ngramContext_b_a.getNextNgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); assertTrue(ngramContext_bos_b.isBeginningOfSentenceContext()); assertEquals("b", ngramContext_bos_b.getNthPrevWord(2)); final NgramContext ngramContext_c_bos = diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index 7a3233625..bcf016ae9 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -30,11 +30,12 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.NgramContextUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.ScriptUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; import java.util.Locale; @@ -136,7 +137,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { } } - private class MockInputMethodService extends InputMethodService { + static class MockInputMethodService extends InputMethodService { private MockConnection mMockConnection; public void setInputConnection(final MockConnection mockConnection) { mMockConnection = mockConnection; @@ -221,7 +222,6 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE }); final SpacingAndPunctuations TAB = new SpacingAndPunctuations( mSpacingAndPunctuations, new int[] { Constants.CODE_TAB }); - final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t"); // A character that needs surrogate pair to represent its code point (U+2008A). final String SUPPLEMENTARY_CHAR_STRING = "\uD840\uDC8A"; final SpacingAndPunctuations SUPPLEMENTARY_CHAR = new SpacingAndPunctuations( diff --git a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java index 8ba0174b5..59bb5f8a4 100644 --- a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java +++ b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java @@ -16,13 +16,11 @@ package com.android.inputmethod.latin; -import android.os.Build; import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.inputmethod.EditorInfo; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.common.Constants; @LargeTest public class ShiftModeTests extends InputTestsBase { diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 221541e4a..90db75e39 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -60,7 +60,7 @@ public class SuggestedWordsTests extends AndroidTestCase { } // Helper for testGetTransformedWordInfo - private SuggestedWordInfo transformWordInfo(final String info, + private static SuggestedWordInfo transformWordInfo(final String info, final int trailingSingleQuotesCount) { final SuggestedWordInfo suggestedWordInfo = createTypedWordInfo(info); final SuggestedWordInfo returnedWordInfo = diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java index c44544f3d..20256e670 100644 --- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java +++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java @@ -19,8 +19,9 @@ package com.android.inputmethod.latin; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.StringUtils; /** * Unit tests for WordComposer. diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/common/InputPointersTests.java index 1a47cddf4..6b3490de8 100644 --- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java +++ b/tests/src/com/android/inputmethod/latin/common/InputPointersTests.java @@ -14,13 +14,11 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.common; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.utils.ResizableIntArray; - import java.util.Arrays; @SmallTest diff --git a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/common/ResizableIntArrayTests.java index 8f58e6873..bd1629faf 100644 --- a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java +++ b/tests/src/com/android/inputmethod/latin/common/ResizableIntArrayTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; +package com.android.inputmethod.latin.common; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -79,13 +79,13 @@ public class ResizableIntArrayTests extends AndroidTestCase { public void testGet() { final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); try { - final int value = src.get(0); + src.get(0); fail("get(0) shouldn't succeed"); } catch (ArrayIndexOutOfBoundsException e) { // success } try { - final int value = src.get(DEFAULT_CAPACITY); + src.get(DEFAULT_CAPACITY); fail("get(DEFAULT_CAPACITY) shouldn't succeed"); } catch (ArrayIndexOutOfBoundsException e) { // success @@ -98,7 +98,7 @@ public class ResizableIntArrayTests extends AndroidTestCase { assertEquals("value after add at " + index, valueAddAt, src.get(index)); assertEquals("value after add at 0", 0, src.get(0)); try { - final int value = src.get(src.getLength()); + src.get(src.getLength()); fail("get(length) shouldn't succeed"); } catch (ArrayIndexOutOfBoundsException e) { // success diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index 637ea4ec8..6c60fdc0c 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -23,8 +23,10 @@ import android.util.Pair; import android.util.SparseArray; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.common.CodePointUtils; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; +import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; @@ -66,6 +68,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { private static final SparseArray<List<Integer>> sChainBigrams = new SparseArray<>(); private static final HashMap<String, List<String>> sShortcuts = new HashMap<>(); + final Random mRandom; + public BinaryDictDecoderEncoderTests() { this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); } @@ -74,10 +78,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { super(); BinaryDictionaryUtils.setCurrentTimeForTest(0); Log.e(TAG, "Testing dictionary: seed is " + seed); - final Random random = new Random(seed); + mRandom = new Random(seed); sWords.clear(); sWordsWithVariousCodePoints.clear(); - generateWords(maxUnigrams, random); + generateWords(maxUnigrams, mRandom); for (int i = 0; i < sWords.size(); ++i) { sChainBigrams.put(i, new ArrayList<Integer>()); @@ -95,10 +99,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { sShortcuts.clear(); for (int i = 0; i < NUM_OF_NODES_HAVING_SHORTCUTS; ++i) { - final int from = Math.abs(random.nextInt()) % sWords.size(); + final int from = Math.abs(mRandom.nextInt()) % sWords.size(); sShortcuts.put(sWords.get(from), new ArrayList<String>()); for (int j = 0; j < NUM_OF_SHORTCUTS; ++j) { - final int to = Math.abs(random.nextInt()) % sWords.size(); + final int to = Math.abs(mRandom.nextInt()) % sWords.size(); sShortcuts.get(sWords.get(from)).add(sWords.get(to)); } } @@ -117,7 +121,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { super.tearDown(); } - private void generateWords(final int number, final Random random) { + private static void generateWords(final int number, final Random random) { final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, random); final Set<String> wordSet = new HashSet<>(); @@ -138,7 +142,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { /** * Adds unigrams to the dictionary. */ - private void addUnigrams(final int number, final FusionDictionary dict, + private static void addUnigrams(final int number, final FusionDictionary dict, final List<String> words, final HashMap<String, List<String>> shortcutMap) { for (int i = 0; i < number; ++i) { final String word = words.get(i); @@ -149,11 +153,12 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } } dict.add(word, new ProbabilityInfo(UNIGRAM_FREQ), - (shortcutMap == null) ? null : shortcuts, false /* isNotAWord */); + (shortcutMap == null) ? null : shortcuts, false /* isNotAWord */, + false /* isPossiblyOffensive */); } } - private void addBigrams(final FusionDictionary dict, + private static void addBigrams(final FusionDictionary dict, final List<String> words, final SparseArray<List<Integer>> bigrams) { for (int i = 0; i < bigrams.size(); ++i) { @@ -172,7 +177,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { // new java.io.FileWriter(new File(filename)), dict); // } - private long timeWritingDictToFile(final File file, final FusionDictionary dict, + private static long timeWritingDictToFile(final File file, final FusionDictionary dict, final FormatSpec.FormatOptions formatOptions) { long now = -1, diff = -1; @@ -195,7 +200,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { return diff; } - private void checkDictionary(final FusionDictionary dict, final List<String> words, + private static void checkDictionary(final FusionDictionary dict, final List<String> words, final SparseArray<List<Integer>> bigrams, final HashMap<String, List<String>> shortcutMap) { assertNotNull(dict); @@ -230,16 +235,16 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } } - private String outputOptions(final int bufferType, + private static String outputOptions(final int bufferType, final FormatSpec.FormatOptions formatOptions) { - String result = " : buffer type = " + final String result = " : buffer type = " + ((bufferType == BinaryDictUtils.USE_BYTE_BUFFER) ? "byte buffer" : "byte array"); return result + " : version = " + formatOptions.mVersion; } // Tests for readDictionaryBinary and writeDictionaryBinary - private long timeReadingAndCheckDict(final File file, final List<String> words, + private static long timeReadingAndCheckDict(final File file, final List<String> words, final SparseArray<List<Integer>> bigrams, final HashMap<String, List<String>> shortcutMap, final int bufferType) { long now, diff = -1; @@ -312,14 +317,14 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final String dictVersion = Long.toString(System.currentTimeMillis()); final String codePointTableAttribute = DictionaryHeader.CODE_POINT_TABLE_KEY; final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, - BinaryDictUtils.VERSION201_OPTIONS, getContext().getCacheDir()); + BinaryDictUtils.STATIC_OPTIONS, getContext().getCacheDir()); // Write a test dictionary final DictEncoder dictEncoder = new Ver2DictEncoder(file, Ver2DictEncoder.CODE_POINT_TABLE_ON); final FormatSpec.FormatOptions formatOptions = new FormatSpec.FormatOptions( - FormatSpec.MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE); + FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION); final FusionDictionary sourcedict = new FusionDictionary(new PtNodeArray(), BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions)); addUnigrams(words.size(), sourcedict, words, null /* shortcutMap */); @@ -357,11 +362,11 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final List<String> results = new ArrayList<>(); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION2_OPTIONS); + BinaryDictUtils.STATIC_OPTIONS); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP); + BinaryDictUtils.DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP); + BinaryDictUtils.DYNAMIC_OPTIONS_WITH_TIMESTAMP); for (final String result : results) { Log.d(TAG, result); } @@ -371,11 +376,11 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final List<String> results = new ArrayList<>(); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION2_OPTIONS); + BinaryDictUtils.STATIC_OPTIONS); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP); + BinaryDictUtils.DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP); + BinaryDictUtils.DYNAMIC_OPTIONS_WITH_TIMESTAMP); for (final String result : results) { Log.d(TAG, result); @@ -384,7 +389,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { // Tests for readUnigramsAndBigramsBinary - private void checkWordMap(final List<String> expectedWords, + private static void checkWordMap(final List<String> expectedWords, final SparseArray<List<Integer>> expectedBigrams, final TreeMap<Integer, String> resultWords, final TreeMap<Integer, Integer> resultFrequencies, @@ -433,9 +438,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { assertEquals(actBigrams, expBigrams); } - private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words, - final SparseArray<List<Integer>> bigrams, final int bufferType, - final boolean checkProbability) { + private static long timeAndCheckReadUnigramsAndBigramsBinary(final File file, + final List<String> words, final SparseArray<List<Integer>> bigrams, + final int bufferType, final boolean checkProbability) { final TreeMap<Integer, String> resultWords = new TreeMap<>(); final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams = new TreeMap<>(); final TreeMap<Integer, Integer> resultFreqs = new TreeMap<>(); @@ -499,7 +504,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final ArrayList<String> results = new ArrayList<>(); runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION2_OPTIONS); + BinaryDictUtils.STATIC_OPTIONS); for (final String result : results) { Log.d(TAG, result); @@ -510,7 +515,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final ArrayList<String> results = new ArrayList<>(); runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION2_OPTIONS); + BinaryDictUtils.STATIC_OPTIONS); for (final String result : results) { Log.d(TAG, result); @@ -518,7 +523,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } // Tests for getTerminalPosition - private String getWordFromBinary(final DictDecoder dictDecoder, final int address) { + private static String getWordFromBinary(final DictDecoder dictDecoder, final int address) { if (dictDecoder.getPosition() != 0) dictDecoder.setPosition(0); DictionaryHeader fileHeader = null; @@ -534,7 +539,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { address).mWord; } - private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word, + private static long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word, final boolean contained) { long diff = -1; int position = -1; @@ -602,11 +607,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { + " : " + outputOptions(bufferType, formatOptions)); // Test a word that isn't contained within the dictionary. - final Random random = new Random((int)System.currentTimeMillis()); final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, - random); + mRandom); for (int i = 0; i < 1000; ++i) { - final String word = CodePointUtils.generateWord(random, codePointSet); + final String word = CodePointUtils.generateWord(mRandom, codePointSet); if (sWords.indexOf(word) != -1) continue; checkGetTerminalPosition(dictDecoder, word, false); } @@ -621,9 +625,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final ArrayList<String> results = new ArrayList<>(); runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION2_OPTIONS); + BinaryDictUtils.STATIC_OPTIONS); runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION2_OPTIONS); + BinaryDictUtils.STATIC_OPTIONS); for (final String result : results) { Log.d(TAG, result); @@ -631,7 +635,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } public void testVer2DictGetWordProperty() { - final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS; + final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS; final ArrayList<String> words = sWords; final HashMap<String, List<String>> shortcuts = sShortcuts; final String dictName = "testGetWordProperty"; @@ -667,7 +671,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } public void testVer2DictIteration() { - final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS; + final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS; final ArrayList<String> words = sWords; final HashMap<String, List<String>> shortcuts = sShortcuts; final SparseArray<List<Integer>> bigrams = sEmptyBigrams; diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 1f3ee19af..be75565bb 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -17,11 +17,16 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; + import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.LinkedList; + +import javax.annotation.Nonnull; /** * Decodes binary files for a FusionDictionary. @@ -113,15 +118,16 @@ public final class BinaryDictDecoderUtils { /** * Helper method to find out whether this code fits on one byte */ - private static boolean fitsOnOneByte(int character, + private static boolean fitsOnOneByte(final int character, final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + int codePoint = character; if (codePointToOneByteCodeMap != null) { if (codePointToOneByteCodeMap.containsKey(character)) { - character = codePointToOneByteCodeMap.get(character); + codePoint = codePointToOneByteCodeMap.get(character); } } - return character >= FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE - && character <= FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE; + return codePoint >= FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE + && codePoint <= FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE; } /** @@ -164,12 +170,13 @@ public final class BinaryDictDecoderUtils { * * @param codePoints the code point array to write. * @param buffer the byte buffer to write to. - * @param index the index in buffer to write the character array to. + * @param fromIndex the index in buffer to write the character array to. * @param codePointToOneByteCodeMap the map to convert the code point. * @return the index after the last character. */ - static int writeCharArray(final int[] codePoints, final byte[] buffer, int index, + static int writeCharArray(final int[] codePoints, final byte[] buffer, final int fromIndex, final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + int index = fromIndex; for (int codePoint : codePoints) { if (codePointToOneByteCodeMap != null) { if (codePointToOneByteCodeMap.containsKey(codePoint)) { @@ -293,10 +300,9 @@ public final class BinaryDictDecoderUtils { final int msb = dictBuffer.readUnsignedByte(); if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= msb) { return msb; - } else { - return ((FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT & msb) << 8) - + dictBuffer.readUnsignedByte(); } + return ((FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT & msb) << 8) + + dictBuffer.readUnsignedByte(); } /** @@ -360,6 +366,43 @@ public final class BinaryDictDecoderUtils { } /** + * Helper method that brutally decodes a header from a byte array. + * + * @param headerBuffer a buffer containing the bytes of the header. + * @return a hashmap of the attributes stored in the header + */ + @Nonnull + public static HashMap<String, String> decodeHeaderAttributes(@Nonnull final byte[] headerBuffer) + throws UnsupportedFormatException { + final StringBuilder sb = new StringBuilder(); + final LinkedList<String> keyValues = new LinkedList<>(); + int index = 0; + while (index < headerBuffer.length) { + if (headerBuffer[index] == FormatSpec.PTNODE_CHARACTERS_TERMINATOR) { + keyValues.add(sb.toString()); + sb.setLength(0); + } else if (CharEncoding.fitsOnOneByte(headerBuffer[index] & 0xFF, + null /* codePointTable */)) { + sb.appendCodePoint(headerBuffer[index] & 0xFF); + } else { + sb.appendCodePoint(((headerBuffer[index] & 0xFF) << 16) + + ((headerBuffer[index + 1] & 0xFF) << 8) + + (headerBuffer[index + 2] & 0xFF)); + index += 2; + } + index += 1; + } + if ((keyValues.size() & 1) != 0) { + throw new UnsupportedFormatException("Odd number of attributes"); + } + final HashMap<String, String> attributes = new HashMap<>(); + for (int i = 0; i < keyValues.size(); i += 2) { + attributes.put(keyValues.get(i), keyValues.get(i + 1)); + } + return attributes; + } + + /** * Helper method to pass a file name instead of a File object to isBinaryDictionary. */ public static boolean isBinaryDictionary(final String filename) { diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index 2d536d822..ce905c499 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -199,8 +199,9 @@ public class BinaryDictEncoderUtils { } } - static int writeUIntToBuffer(final byte[] buffer, int position, final int value, + static int writeUIntToBuffer(final byte[] buffer, final int fromPosition, final int value, final int size) { + int position = fromPosition; switch(size) { case 4: buffer[position++] = (byte) ((value >> 24) & 0xFF); @@ -324,11 +325,9 @@ public class BinaryDictEncoderUtils { return targetNodeArray.mCachedAddressAfterUpdate - (currentNodeArray.mCachedAddressAfterUpdate + offsetFromStartOfCurrentNodeArray); - } else { - return targetNodeArray.mCachedAddressBeforeUpdate - - (currentNodeArray.mCachedAddressBeforeUpdate - + offsetFromStartOfCurrentNodeArray); } + return targetNodeArray.mCachedAddressBeforeUpdate + - (currentNodeArray.mCachedAddressBeforeUpdate + offsetFromStartOfCurrentNodeArray); } /** @@ -356,9 +355,8 @@ public class BinaryDictEncoderUtils { final int newOffsetBasePoint = currentNodeArray.mCachedAddressAfterUpdate + offsetFromStartOfCurrentNodeArray; return targetPtNode.mCachedAddressAfterUpdate - newOffsetBasePoint; - } else { - return targetPtNode.mCachedAddressBeforeUpdate - oldOffsetBasePoint; } + return targetPtNode.mCachedAddressBeforeUpdate - oldOffsetBasePoint; } /** @@ -537,12 +535,13 @@ public class BinaryDictEncoderUtils { * Helper method to write a children position to a file. * * @param buffer the buffer to write to. - * @param index the index in the buffer to write the address to. + * @param fromIndex the index in the buffer to write the address to. * @param position the position to write. * @return the size in bytes the address actually took. */ - /* package */ static int writeChildrenPosition(final byte[] buffer, int index, + /* package */ static int writeChildrenPosition(final byte[] buffer, final int fromIndex, final int position) { + int index = fromIndex; switch (getByteSize(position)) { case 1: buffer[index++] = (byte)position; @@ -572,12 +571,12 @@ public class BinaryDictEncoderUtils { * @param hasShortcuts whether the PtNode has shortcuts. * @param hasBigrams whether the PtNode has bigrams. * @param isNotAWord whether the PtNode is not a word. - * @param isBlackListEntry whether the PtNode is a blacklist entry. + * @param isPossiblyOffensive whether the PtNode is a possibly offensive entry. * @return the flags */ static int makePtNodeFlags(final boolean hasMultipleChars, final boolean isTerminal, final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams, - final boolean isNotAWord, final boolean isBlackListEntry) { + final boolean isNotAWord, final boolean isPossiblyOffensive) { byte flags = 0; if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS; if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL; @@ -600,7 +599,7 @@ public class BinaryDictEncoderUtils { if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS; if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS; if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD; - if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED; + if (isPossiblyOffensive) flags |= FormatSpec.FLAG_IS_POSSIBLY_OFFENSIVE; return flags; } @@ -609,7 +608,7 @@ public class BinaryDictEncoderUtils { getByteSize(childrenOffset), node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(), node.mBigrams != null && !node.mBigrams.isEmpty(), - node.mIsNotAWord, node.mIsBlacklistEntry); + node.mIsNotAWord, node.mIsPossiblyOffensive); } /** @@ -623,7 +622,7 @@ public class BinaryDictEncoderUtils { * @return the flags */ /* package */ static final int makeBigramFlags(final boolean more, final int offset, - int bigramFrequency, final int unigramFrequency, final String word) { + final int bigramFrequency, final int unigramFrequency, final String word) { int bigramFlags = (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0) + (offset < 0 ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0); switch (getByteSize(offset)) { @@ -639,13 +638,16 @@ public class BinaryDictEncoderUtils { default: throw new RuntimeException("Strange offset size"); } + final int frequency; if (unigramFrequency > bigramFrequency) { MakedictLog.e("Unigram freq is superior to bigram freq for \"" + word + "\". Bigram freq is " + bigramFrequency + ", unigram freq for " + word + " is " + unigramFrequency); - bigramFrequency = unigramFrequency; + frequency = unigramFrequency; + } else { + frequency = bigramFrequency; } - bigramFlags += getBigramFrequencyDiff(unigramFrequency, bigramFrequency) + bigramFlags += getBigramFrequencyDiff(unigramFrequency, frequency) & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY; return bigramFlags; } @@ -722,7 +724,6 @@ public class BinaryDictEncoderUtils { * @param ptNodeArray the node array to write. * @param codePointToOneByteCodeMap the map to convert the code points. */ - @SuppressWarnings("unused") /* package */ static void writePlacedPtNodeArray(final FusionDictionary dict, final DictEncoder dictEncoder, final PtNodeArray ptNodeArray, final HashMap<Integer, Integer> codePointToOneByteCodeMap) { @@ -818,12 +819,18 @@ public class BinaryDictEncoderUtils { final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray) throws IOException, UnsupportedFormatException { final int version = formatOptions.mVersion; - if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION - || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) { + if ((version >= FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION && + version <= FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION) || ( + version >= FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION && + version <= FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION)) { + // Dictionary is valid + } else { throw new UnsupportedFormatException("Requested file format version " + version - + ", but this implementation only supports versions " - + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through " - + FormatSpec.MAXIMUM_SUPPORTED_VERSION); + + ", but this implementation only supports static versions " + + FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION + " through " + + FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION + " and dynamic versions " + + FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION + " through " + + FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION); } ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256); diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 9c3b37387..b104a21f9 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -17,7 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory; import java.io.File; @@ -44,7 +44,7 @@ public final class BinaryDictIOUtils { public static DictDecoder getDictDecoder(final File dictFile, final long offset, final long length, final int bufferType) { if (dictFile.isDirectory()) { - return new Ver4DictDecoder(dictFile, bufferType); + return new Ver4DictDecoder(dictFile); } else if (dictFile.isFile()) { return new Ver2DictDecoder(dictFile, offset, length, bufferType); } @@ -54,7 +54,7 @@ public final class BinaryDictIOUtils { public static DictDecoder getDictDecoder(final File dictFile, final long offset, final long length, final DictionaryBufferFactory factory) { if (dictFile.isDirectory()) { - return new Ver4DictDecoder(dictFile, factory); + return new Ver4DictDecoder(dictFile); } else if (dictFile.isFile()) { return new Ver2DictDecoder(dictFile, offset, length, factory); } @@ -206,11 +206,7 @@ public final class BinaryDictIOUtils { if (same) { // found the PtNode matches the word. if (wordPos + currentInfo.mCharacters.length == wordLen) { - if (!currentInfo.isTerminal()) { - return FormatSpec.NOT_VALID_WORD; - } else { - return ptNodePos; - } + return currentInfo.isTerminal() ? ptNodePos : FormatSpec.NOT_VALID_WORD; } wordPos += currentInfo.mCharacters.length; if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) { diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java index 8eabf749d..9c1e4cf84 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java @@ -28,13 +28,11 @@ public class BinaryDictUtils { public static final String TEST_DICT_FILE_EXTENSION = ".testDict"; - public static final FormatSpec.FormatOptions VERSION2_OPTIONS = - new FormatSpec.FormatOptions(FormatSpec.VERSION2); - public static final FormatSpec.FormatOptions VERSION201_OPTIONS = - new FormatSpec.FormatOptions(FormatSpec.VERSION201); - public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITHOUT_TIMESTAMP = + public static final FormatSpec.FormatOptions STATIC_OPTIONS = + new FormatSpec.FormatOptions(FormatSpec.VERSION202); + public static final FormatSpec.FormatOptions DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP = new FormatSpec.FormatOptions(FormatSpec.VERSION4, false /* hasTimestamp */); - public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITH_TIMESTAMP = + public static final FormatSpec.FormatOptions DYNAMIC_OPTIONS_WITH_TIMESTAMP = new FormatSpec.FormatOptions(FormatSpec.VERSION4, true /* hasTimestamp */); public static DictionaryOptions makeDictionaryOptions(final String id, final String version, @@ -55,7 +53,8 @@ public class BinaryDictUtils { public static File getDictFile(final String name, final String version, final FormatOptions formatOptions, final File directory) { if (formatOptions.mVersion == FormatSpec.VERSION2 - || formatOptions.mVersion == FormatSpec.VERSION201) { + || formatOptions.mVersion == FormatSpec.VERSION201 + || formatOptions.mVersion == FormatSpec.VERSION202) { return new File(directory, name + "." + version + TEST_DICT_FILE_EXTENSION); } else if (formatOptions.mVersion == FormatSpec.VERSION4) { return new File(directory, name + "." + version); @@ -71,7 +70,7 @@ public class BinaryDictUtils { file.mkdir(); } return new Ver4DictEncoder(file); - } else if (formatOptions.mVersion == FormatSpec.VERSION2) { + } else if (formatOptions.mVersion == FormatSpec.VERSION202) { return new Ver2DictEncoder(file, Ver2DictEncoder.CODE_POINT_TABLE_OFF); } else { throw new RuntimeException("The format option has a wrong version : " diff --git a/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 4a8c178b5..3cffd001c 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -17,7 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; import java.util.ArrayList; @@ -89,7 +89,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { int mTerminalId; // NOT_A_TERMINAL == mTerminalId indicates this is not a terminal. PtNodeArray mChildren; boolean mIsNotAWord; // Only a shortcut - boolean mIsBlacklistEntry; + boolean mIsPossiblyOffensive; // mCachedSize and mCachedAddressBefore/AfterUpdate are helpers for binary dictionary // generation. Before and After always hold the same value except during dictionary // address compression, where the update process needs to know about both values at the @@ -102,7 +102,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets, final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo, - final boolean isNotAWord, final boolean isBlacklistEntry) { + final boolean isNotAWord, final boolean isPossiblyOffensive) { mChars = chars; mProbabilityInfo = probabilityInfo; mTerminalId = probabilityInfo == null ? NOT_A_TERMINAL : probabilityInfo.mProbability; @@ -110,12 +110,12 @@ public final class FusionDictionary implements Iterable<WordProperty> { mBigrams = bigrams; mChildren = null; mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; + mIsPossiblyOffensive = isPossiblyOffensive; } public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets, final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo, - final boolean isNotAWord, final boolean isBlacklistEntry, + final boolean isNotAWord, final boolean isPossiblyOffensive, final PtNodeArray children) { mChars = chars; mProbabilityInfo = probabilityInfo; @@ -123,7 +123,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { mBigrams = bigrams; mChildren = children; mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; + mIsPossiblyOffensive = isPossiblyOffensive; } public void addChild(PtNode n) { @@ -142,19 +142,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { } public int getProbability() { - if (isTerminal()) { - return mProbabilityInfo.mProbability; - } else { - return NOT_A_TERMINAL; - } + return isTerminal() ? mProbabilityInfo.mProbability : NOT_A_TERMINAL; } public boolean getIsNotAWord() { return mIsNotAWord; } - public boolean getIsBlacklistEntry() { - return mIsBlacklistEntry; + public boolean getIsPossiblyOffensive() { + return mIsPossiblyOffensive; } public ArrayList<WeightedString> getShortcutTargets() { @@ -235,10 +231,10 @@ public final class FusionDictionary implements Iterable<WordProperty> { * the existing ones if any. Note: unigram, bigram, and shortcut frequencies are only * updated if they are higher than the existing ones. */ - private void update(final ProbabilityInfo probabilityInfo, + void update(final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> shortcutTargets, final ArrayList<WeightedString> bigrams, - final boolean isNotAWord, final boolean isBlacklistEntry) { + final boolean isNotAWord, final boolean isPossiblyOffensive) { mProbabilityInfo = ProbabilityInfo.max(mProbabilityInfo, probabilityInfo); if (shortcutTargets != null) { if (mShortcutTargets == null) { @@ -275,7 +271,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { } } mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; + mIsPossiblyOffensive = isPossiblyOffensive; } } @@ -323,24 +319,12 @@ public final class FusionDictionary implements Iterable<WordProperty> { * @param probabilityInfo probability information of the word. * @param shortcutTargets a list of shortcut targets for this word, or null. * @param isNotAWord true if this should not be considered a word (e.g. shortcut only) + * @param isPossiblyOffensive true if this word is possibly offensive */ public void add(final String word, final ProbabilityInfo probabilityInfo, - final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) { - add(getCodePoints(word), probabilityInfo, shortcutTargets, isNotAWord, - false /* isBlacklistEntry */); - } - - /** - * Helper method to add a blacklist entry as a string. - * - * @param word the word to add as a blacklist entry. - * @param shortcutTargets a list of shortcut targets for this word, or null. - * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so) - */ - public void addBlacklistEntry(final String word, - final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) { - add(getCodePoints(word), new ProbabilityInfo(0), shortcutTargets, isNotAWord, - true /* isBlacklistEntry */); + final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord, + final boolean isPossiblyOffensive) { + add(getCodePoints(word), probabilityInfo, shortcutTargets, isNotAWord, isPossiblyOffensive); } /** @@ -349,15 +333,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { * This method checks that all PtNodes in a node array are ordered as expected. * If they are, nothing happens. If they aren't, an exception is thrown. */ - private void checkStack(PtNodeArray ptNodeArray) { + private static void checkStack(PtNodeArray ptNodeArray) { ArrayList<PtNode> stack = ptNodeArray.mData; int lastValue = -1; for (int i = 0; i < stack.size(); ++i) { int currentValue = stack.get(i).mChars[0]; - if (currentValue <= lastValue) + if (currentValue <= lastValue) { throw new RuntimeException("Invalid stack"); - else - lastValue = currentValue; + } + lastValue = currentValue; } } @@ -375,7 +359,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { final PtNode ptNode1 = findWordInTree(mRootNodeArray, word1); if (ptNode1 == null) { add(getCodePoints(word1), new ProbabilityInfo(0), null, false /* isNotAWord */, - false /* isBlacklistEntry */); + false /* isPossiblyOffensive */); // The PtNode for the first word may have moved by the above insertion, // if word1 and word2 share a common stem that happens not to have been // a cutting point until now. In this case, we need to refresh ptNode. @@ -397,11 +381,11 @@ public final class FusionDictionary implements Iterable<WordProperty> { * @param probabilityInfo the probability information of the word. * @param shortcutTargets an optional list of shortcut targets for this word (null if none). * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so) - * @param isBlacklistEntry true if this is a blacklisted word, false otherwise + * @param isPossiblyOffensive true if this word is possibly offensive */ private void add(final int[] word, final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> shortcutTargets, - final boolean isNotAWord, final boolean isBlacklistEntry) { + final boolean isNotAWord, final boolean isPossiblyOffensive) { assert(probabilityInfo.mProbability <= FormatSpec.MAX_TERMINAL_FREQUENCY); if (word.length >= Constants.DICTIONARY_MAX_WORD_LENGTH) { MakedictLog.w("Ignoring a word that is too long: word.length = " + word.length); @@ -431,7 +415,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { final int insertionIndex = findInsertionIndex(currentNodeArray, word[charIndex]); final PtNode newPtNode = new PtNode(Arrays.copyOfRange(word, charIndex, word.length), shortcutTargets, null /* bigrams */, probabilityInfo, isNotAWord, - isBlacklistEntry); + isPossiblyOffensive); currentNodeArray.mData.add(insertionIndex, newPtNode); if (DBG) checkStack(currentNodeArray); } else { @@ -442,14 +426,14 @@ public final class FusionDictionary implements Iterable<WordProperty> { // should end already exists as is. Since the old PtNode was not a terminal, // make it one by filling in its frequency and other attributes currentPtNode.update(probabilityInfo, shortcutTargets, null, isNotAWord, - isBlacklistEntry); + isPossiblyOffensive); } else { // The new word matches the full old word and extends past it. // We only have to create a new node and add it to the end of this. final PtNode newNode = new PtNode( Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), shortcutTargets, null /* bigrams */, probabilityInfo, - isNotAWord, isBlacklistEntry); + isNotAWord, isPossiblyOffensive); currentPtNode.mChildren = new PtNodeArray(); currentPtNode.mChildren.mData.add(newNode); } @@ -459,7 +443,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { // new shortcuts to the existing shortcut list if it already exists. currentPtNode.update(probabilityInfo, shortcutTargets, null, currentPtNode.mIsNotAWord && isNotAWord, - currentPtNode.mIsBlacklistEntry || isBlacklistEntry); + currentPtNode.mIsPossiblyOffensive || isPossiblyOffensive); } else { // Partial prefix match only. We have to replace the current node with a node // containing the current prefix and create two new ones for the tails. @@ -468,7 +452,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { Arrays.copyOfRange(currentPtNode.mChars, differentCharIndex, currentPtNode.mChars.length), currentPtNode.mShortcutTargets, currentPtNode.mBigrams, currentPtNode.mProbabilityInfo, - currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry, + currentPtNode.mIsNotAWord, currentPtNode.mIsPossiblyOffensive, currentPtNode.mChildren); newChildren.mData.add(newOldWord); @@ -477,17 +461,17 @@ public final class FusionDictionary implements Iterable<WordProperty> { newParent = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex), shortcutTargets, null /* bigrams */, probabilityInfo, - isNotAWord, isBlacklistEntry, newChildren); + isNotAWord, isPossiblyOffensive, newChildren); } else { newParent = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex), null /* shortcutTargets */, null /* bigrams */, null /* probabilityInfo */, false /* isNotAWord */, - false /* isBlacklistEntry */, newChildren); + false /* isPossiblyOffensive */, newChildren); final PtNode newWord = new PtNode(Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), shortcutTargets, null /* bigrams */, probabilityInfo, - isNotAWord, isBlacklistEntry); + isNotAWord, isPossiblyOffensive); final int addIndex = word[charIndex + differentCharIndex] > currentPtNode.mChars[differentCharIndex] ? 1 : 0; newChildren.mData.add(addIndex, newWord); @@ -533,14 +517,14 @@ public final class FusionDictionary implements Iterable<WordProperty> { * is ignored. * This comparator imposes orderings that are inconsistent with equals. */ - static private final class PtNodeComparator implements java.util.Comparator<PtNode> { + static final class PtNodeComparator implements java.util.Comparator<PtNode> { @Override public int compare(PtNode p1, PtNode p2) { if (p1.mChars[0] == p2.mChars[0]) return 0; return p1.mChars[0] < p2.mChars[0] ? -1 : 1; } } - final static private PtNodeComparator PTNODE_COMPARATOR = new PtNodeComparator(); + final static PtNodeComparator PTNODE_COMPARATOR = new PtNodeComparator(); /** * Finds the insertion index of a character within a node array. @@ -549,7 +533,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { final ArrayList<PtNode> data = nodeArray.mData; final PtNode reference = new PtNode(new int[] { character }, null /* shortcutTargets */, null /* bigrams */, null /* probabilityInfo */, - false /* isNotAWord */, false /* isBlacklistEntry */); + false /* isNotAWord */, false /* isPossiblyOffensive */); int result = Collections.binarySearch(data, reference, PTNODE_COMPARATOR); return result >= 0 ? result : -result - 1; } @@ -571,7 +555,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { /** * Helper method to find a word in a given branch. */ - public static PtNode findWordInTree(PtNodeArray nodeArray, final String string) { + public static PtNode findWordInTree(final PtNodeArray rootNodeArray, final String string) { + PtNodeArray nodeArray = rootNodeArray; int index = 0; final StringBuilder checker = DBG ? new StringBuilder() : null; final int[] codePoints = getCodePoints(string); @@ -686,7 +671,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { return new WordProperty(mCurrentString.toString(), currentPtNode.mProbabilityInfo, currentPtNode.mShortcutTargets, currentPtNode.mBigrams, - currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry); + currentPtNode.mIsNotAWord, currentPtNode.mIsPossiblyOffensive); } } else { mPositions.removeLast(); diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java index 6227f13e1..5c261a94d 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java @@ -36,17 +36,17 @@ public class Ver2DictDecoder extends AbstractDictDecoder { /** * A utility class for reading a PtNode. */ - protected static class PtNodeReader { - private static ProbabilityInfo readProbabilityInfo(final DictBuffer dictBuffer) { + static class PtNodeReader { + static ProbabilityInfo readProbabilityInfo(final DictBuffer dictBuffer) { // Ver2 dicts don't contain historical information. return new ProbabilityInfo(dictBuffer.readUnsignedByte()); } - protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { + static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { return dictBuffer.readUnsignedByte(); } - protected static int readChildrenAddress(final DictBuffer dictBuffer, + static int readChildrenAddress(final DictBuffer dictBuffer, final int ptNodeFlags) { switch (ptNodeFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: @@ -62,7 +62,7 @@ public class Ver2DictDecoder extends AbstractDictDecoder { } // Reads shortcuts and returns the read length. - protected static int readShortcut(final DictBuffer dictBuffer, + static int readShortcut(final DictBuffer dictBuffer, final ArrayList<WeightedString> shortcutTargets) { final int pointerBefore = dictBuffer.position(); dictBuffer.readUnsignedShort(); // skip the size @@ -76,7 +76,7 @@ public class Ver2DictDecoder extends AbstractDictDecoder { return dictBuffer.position() - pointerBefore; } - protected static int readBigramAddresses(final DictBuffer dictBuffer, + static int readBigramAddresses(final DictBuffer dictBuffer, final ArrayList<PendingAttribute> bigrams, final int baseAddress) { int readLength = 0; int bigramCount = 0; @@ -178,7 +178,8 @@ public class Ver2DictDecoder extends AbstractDictDecoder { throw new IOException("Cannot read the dictionary header."); } if (header.mFormatOptions.mVersion != FormatSpec.VERSION2 && - header.mFormatOptions.mVersion != FormatSpec.VERSION201) { + header.mFormatOptions.mVersion != FormatSpec.VERSION201 && + header.mFormatOptions.mVersion != FormatSpec.VERSION202) { throw new UnsupportedFormatException("File header has a wrong version : " + header.mFormatOptions.mVersion); } @@ -283,13 +284,9 @@ public class Ver2DictDecoder extends AbstractDictDecoder { // Insert unigrams into the fusion dictionary. for (final WordProperty wordProperty : wordProperties) { - if (wordProperty.mIsBlacklistEntry) { - fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets, - wordProperty.mIsNotAWord); - } else { - fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, - wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); - } + fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, + wordProperty.mShortcutTargets, wordProperty.mIsNotAWord, + wordProperty.mIsPossiblyOffensive); } // Insert bigrams into the fusion dictionary. for (final WordProperty wordProperty : wordProperties) { diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java index 2c2152be7..b52b8c485 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java @@ -124,7 +124,8 @@ public class Ver2DictEncoder implements DictEncoder { @Override public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions) throws IOException, UnsupportedFormatException { - if (formatOptions.mVersion > FormatSpec.VERSION201) { + // We no longer support anything but the latest version of v2. + if (formatOptions.mVersion != FormatSpec.VERSION202) { throw new UnsupportedFormatException( "The given format options has wrong version number : " + formatOptions.mVersion); diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java index 9104c2fcb..7d858760e 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java @@ -27,14 +27,12 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; /** * Unit tests for Ver2DictEncoder */ @LargeTest public class Ver2DictEncoderTests extends AndroidTestCase { - private static final String TAG = Ver2DictEncoderTests.class.getSimpleName(); private static final int UNIGRAM_FREQ = 10; public void testCodePointTable() { @@ -75,7 +73,7 @@ public class Ver2DictEncoderTests extends AndroidTestCase { /** * Adds unigrams to the dictionary. */ - private void addUnigrams(final FusionDictionary dict, final List<String> words, + private static void addUnigrams(final FusionDictionary dict, final List<String> words, final HashMap<String, List<String>> shortcutMap) { for (final String word : words) { final ArrayList<WeightedString> shortcuts = new ArrayList<>(); @@ -85,7 +83,8 @@ public class Ver2DictEncoderTests extends AndroidTestCase { } } dict.add(word, new ProbabilityInfo(UNIGRAM_FREQ), - (shortcutMap == null) ? null : shortcuts, false /* isNotAWord */); + (shortcutMap == null) ? null : shortcuts, false /* isNotAWord */, + false /* isPossiblyOffensive */); } } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 0da915a75..7e54ce986 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -33,12 +33,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder { final File mDictDirectory; @UsedForTesting - /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) { - this(dictDirectory, null /* factory */); - } - - @UsedForTesting - /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) { + /* package */ Ver4DictDecoder(final File dictDirectory) { mDictDirectory = dictDirectory; } @@ -88,13 +83,9 @@ public class Ver4DictDecoder extends AbstractDictDecoder { // Insert unigrams into the fusion dictionary. for (final WordProperty wordProperty : wordProperties) { - if (wordProperty.mIsBlacklistEntry) { - fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets, - wordProperty.mIsNotAWord); - } else { - fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, - wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); - } + fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, + wordProperty.mShortcutTargets, wordProperty.mIsNotAWord, + wordProperty.mIsPossiblyOffensive); } // Insert bigrams into the fusion dictionary. // TODO: Support ngrams. diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java index 3262a1623..155421922 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -79,7 +79,7 @@ public class Ver4DictEncoder implements DictEncoder { if (!binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(), null /* shortcutTarget */, 0 /* shortcutProbability */, wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord, - wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) { + wordProperty.mIsPossiblyOffensive, 0 /* timestamp */)) { MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord); } } else { @@ -88,7 +88,7 @@ public class Ver4DictEncoder implements DictEncoder { wordProperty.getProbability(), shortcutTarget.mWord, shortcutTarget.getProbability(), wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord, - wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) { + wordProperty.mIsPossiblyOffensive, 0 /* timestamp */)) { MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord + ", shortcutTarget: " + shortcutTarget.mWord); return; diff --git a/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java b/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java index fed8be920..8f24cdb44 100644 --- a/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java +++ b/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java @@ -128,7 +128,7 @@ public class BlockingHttpClientTests extends AndroidTestCase { assertTrue("ResponseProcessor was not invoked", processor.mInvoked); } - private static class FakeErrorResponseProcessor implements ResponseProcessor<Void> { + static class FakeErrorResponseProcessor implements ResponseProcessor<Void> { @Override public Void onSuccess(InputStream response) { fail("Expected an error but received success"); diff --git a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java index 011309942..f07dac7c0 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java @@ -34,8 +34,6 @@ import android.test.suitebuilder.annotation.LargeTest; */ @LargeTest public class ContextualDictionaryTests extends AndroidTestCase { - private static final String TAG = ContextualDictionaryTests.class.getSimpleName(); - private static final Locale LOCALE_EN_US = new Locale("en", "US"); private DictionaryFacilitator getDictionaryFacilitator() { diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java index afabbbd38..dc6fb0075 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java @@ -30,8 +30,8 @@ import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.ExpandableBinaryDictionary; import com.android.inputmethod.latin.RichInputMethodManager; -import com.android.inputmethod.latin.ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback; -import com.android.inputmethod.latin.makedict.CodePointUtils; +import com.android.inputmethod.latin.ExpandableBinaryDictionary.UpdateEntriesForInputEventsCallback; +import com.android.inputmethod.latin.common.CodePointUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import android.test.AndroidTestCase; @@ -96,8 +96,8 @@ public class PersonalizationDictionaryTests extends AndroidTestCase { true /* inputByUser */, tokens, timeStampInSeconds, DUMMY_PACKAGE_NAME, LOCALE_EN_US.getLanguage()); final CountDownLatch countDownLatch = new CountDownLatch(1); - final AddMultipleDictionaryEntriesCallback callback = - new AddMultipleDictionaryEntriesCallback() { + final UpdateEntriesForInputEventsCallback callback = + new UpdateEntriesForInputEventsCallback() { @Override public void onFinished() { countDownLatch.countDown(); diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index 766627334..778f6e800 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -67,14 +67,14 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { FileUtils.deleteFilteredFiles(dictFile.getParentFile(), filenameFilter); } - private void printAllFiles(final File dir) { + private static void printAllFiles(final File dir) { Log.d(TAG, dir.getAbsolutePath()); for (final File file : dir.listFiles()) { Log.d(TAG, " " + file.getName()); } } - private void checkExistenceAndRemoveDictFile(final UserHistoryDictionary dict, + private static void checkExistenceAndRemoveDictFile(final UserHistoryDictionary dict, final File dictFile) { Log.d(TAG, "waiting for writing ..."); dict.waitAllTasksForTests(); @@ -193,7 +193,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { * Clear all entries in the user history dictionary. * @param dict the user history dictionary. */ - private void clearHistory(final UserHistoryDictionary dict) { + private static void clearHistory(final UserHistoryDictionary dict) { dict.waitAllTasksForTests(); dict.clear(); dict.close(); diff --git a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java index 2ef8b548f..36e967275 100644 --- a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java +++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java @@ -17,7 +17,7 @@ package com.android.inputmethod.latin.settings; import android.app.AlertDialog; -import android.app.Dialog; +import android.content.DialogInterface; import android.content.Intent; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.MediumTest; @@ -33,8 +33,6 @@ public class AccountsSettingsFragmentTests private static final String FRAG_NAME = AccountsSettingsFragment.class.getName(); private static final long TEST_TIMEOUT_MILLIS = 5000; - private AlertDialog mDialog; - public AccountsSettingsFragmentTests() { super(TestFragmentActivity.class); } @@ -58,21 +56,29 @@ public class AccountsSettingsFragmentTests } } + private static class DialogHolder { + AlertDialog mDialog; + DialogHolder() {} + } + public void testMultipleAccounts_noCurrentAccount() { final AccountsSettingsFragment fragment = (AccountsSettingsFragment) getActivity().mFragment; + final DialogHolder dialogHolder = new DialogHolder(); final CountDownLatch latch = new CountDownLatch(1); + getActivity().runOnUiThread(new Runnable() { @Override public void run() { - mDialog = fragment.createAccountPicker( + final AlertDialog dialog = fragment.createAccountPicker( new String[] { "1@example.com", "2@example.com", "3@example.com", "4@example.com"}, null); - mDialog.show(); + dialog.show(); + dialogHolder.mDialog = dialog; latch.countDown(); } }); @@ -83,32 +89,38 @@ public class AccountsSettingsFragmentTests fail(); } getInstrumentation().waitForIdleSync(); - final ListView lv = mDialog.getListView(); + final AlertDialog dialog = dialogHolder.mDialog; + final ListView lv = dialog.getListView(); // The 1st account should be checked by default. assertEquals("checked-item", 0, lv.getCheckedItemPosition()); // There should be 4 accounts in the list. assertEquals("count", 4, lv.getCount()); // The sign-out button shouldn't exist - assertEquals(View.GONE, mDialog.getButton(Dialog.BUTTON_NEUTRAL).getVisibility()); - assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility()); - assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility()); + assertEquals(View.GONE, + dialog.getButton(DialogInterface.BUTTON_NEUTRAL).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_POSITIVE).getVisibility()); } public void testMultipleAccounts_currentAccount() { final AccountsSettingsFragment fragment = (AccountsSettingsFragment) getActivity().mFragment; + final DialogHolder dialogHolder = new DialogHolder(); final CountDownLatch latch = new CountDownLatch(1); getActivity().runOnUiThread(new Runnable() { @Override public void run() { - mDialog = fragment.createAccountPicker( + final AlertDialog dialog = fragment.createAccountPicker( new String[] { "1@example.com", "2@example.com", "3@example.com", "4@example.com"}, "3@example.com"); - mDialog.show(); + dialog.show(); + dialogHolder.mDialog = dialog; latch.countDown(); } }); @@ -119,14 +131,18 @@ public class AccountsSettingsFragmentTests fail(); } getInstrumentation().waitForIdleSync(); - final ListView lv = mDialog.getListView(); + final AlertDialog dialog = dialogHolder.mDialog; + final ListView lv = dialog.getListView(); // The 3rd account should be checked by default. assertEquals("checked-item", 2, lv.getCheckedItemPosition()); // There should be 4 accounts in the list. assertEquals("count", 4, lv.getCount()); // The sign-out button should be shown - assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEUTRAL).getVisibility()); - assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility()); - assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_NEUTRAL).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_POSITIVE).getVisibility()); } } diff --git a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java index eb76032b1..ed632db68 100644 --- a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java +++ b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java @@ -20,9 +20,8 @@ import android.content.res.Resources; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.RunInLocale; import junit.framework.AssertionFailedError; @@ -37,13 +36,11 @@ public class SpacingAndPunctuationsTests extends AndroidTestCase { private int mScreenMetrics; private boolean isPhone() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE; + return Constants.isPhone(mScreenMetrics); } private boolean isTablet() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET; + return Constants.isTablet(mScreenMetrics); } private SpacingAndPunctuations ENGLISH; @@ -70,7 +67,7 @@ public class SpacingAndPunctuationsTests extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics); + mScreenMetrics = Settings.readScreenMetrics(getContext().getResources()); // Language only ENGLISH = getSpacingAndPunctuations(Locale.ENGLISH); diff --git a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java index 66a12b99b..1db839506 100644 --- a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java @@ -16,6 +16,13 @@ package com.android.inputmethod.latin.utils; +import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.ASCII_CAPABLE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; + import android.content.Context; import android.os.Build; import android.test.AndroidTestCase; @@ -26,14 +33,6 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import java.util.Locale; -import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue - .UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; - @SmallTest public class AdditionalSubtypeUtilsTests extends AndroidTestCase { diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java index 1501e942a..170d64383 100644 --- a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java @@ -22,14 +22,14 @@ import android.util.Log; @MediumTest public class AsyncResultHolderTests extends AndroidTestCase { - private static final String TAG = AsyncResultHolderTests.class.getSimpleName(); + static final String TAG = AsyncResultHolderTests.class.getSimpleName(); private static final int TIMEOUT_IN_MILLISECONDS = 500; private static final int MARGIN_IN_MILLISECONDS = 250; private static final int DEFAULT_VALUE = 2; private static final int SET_VALUE = 1; - private <T> void setAfterGivenTime(final AsyncResultHolder<T> holder, final T value, + private static <T> void setAfterGivenTime(final AsyncResultHolder<T> holder, final T value, final long time) { new Thread(new Runnable() { @Override diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java index a333ee9bc..131865ab2 100644 --- a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java @@ -39,10 +39,8 @@ public class BinaryDictionaryUtilsTests extends AndroidTestCase { final int formatVersion) throws IOException { if (formatVersion == FormatSpec.VERSION4) { return createEmptyVer4DictionaryAndGetFile(dictId); - } else { - throw new IOException("Dictionary format version " + formatVersion - + " is not supported."); } + throw new IOException("Dictionary format version " + formatVersion + " is not supported."); } private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException { @@ -59,10 +57,8 @@ public class BinaryDictionaryUtilsTests extends AndroidTestCase { if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), FormatSpec.VERSION4, LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) { return file; - } else { - throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created."); } + throw new IOException("Empty dictionary " + file.getAbsolutePath() + " cannot be created."); } private File getDictFile(final String dictId) { diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java index 76e28288f..dc4e2e4bb 100644 --- a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java @@ -29,27 +29,57 @@ import java.util.Collections; @SmallTest public class CollectionUtilsTests extends AndroidTestCase { /** - * Tests that {@link CollectionUtils#arrayAsList(E[],int,int)} gives the expected + * Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} fails as expected + * with some invalid inputs. + */ + public void testArrayAsListFailure() { + final String[] array = { "0", "1" }; + // Negative start + try { + CollectionUtils.arrayAsList(array, -1, 1); + fail("Failed to catch start < 0"); + } catch (final IllegalArgumentException e) { + assertEquals("Invalid start: -1 end: 1 with array.length: 2", e.getMessage()); + } + // start > end + try { + CollectionUtils.arrayAsList(array, 1, -1); + fail("Failed to catch start > end"); + } catch (final IllegalArgumentException e) { + assertEquals("Invalid start: 1 end: -1 with array.length: 2", e.getMessage()); + } + // end > array.length + try { + CollectionUtils.arrayAsList(array, 1, 3); + fail("Failed to catch end > array.length"); + } catch (final IllegalArgumentException e) { + assertEquals("Invalid start: 1 end: 3 with array.length: 2", e.getMessage()); + } + } + + /** + * Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} gives the expected * results for a few valid inputs. */ public void testArrayAsList() { - final String[] array = { "0", "1", "2", "3", "4" }; final ArrayList<String> empty = new ArrayList<>(); + assertEquals(empty, CollectionUtils.arrayAsList(new String[] { }, 0, 0)); + final String[] array = { "0", "1", "2", "3", "4" }; assertEquals(empty, CollectionUtils.arrayAsList(array, 0, 0)); assertEquals(empty, CollectionUtils.arrayAsList(array, 1, 1)); + assertEquals(empty, CollectionUtils.arrayAsList(array, array.length, array.length)); final ArrayList<String> expected123 = new ArrayList<>(Arrays.asList("1", "2", "3")); assertEquals(expected123, CollectionUtils.arrayAsList(array, 1, 4)); } /** - * Tests that {@link CollectionUtils#isEmpty(java.util.Collection)} gives the expected + * Tests that {@link CollectionUtils#isNullOrEmpty(java.util.Collection)} gives the expected * results for a few cases. */ public void testIsNullOrEmpty() { assertTrue(CollectionUtils.isNullOrEmpty(null)); - assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList())); + assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList<>())); assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_SET)); assertFalse(CollectionUtils.isNullOrEmpty(Collections.singleton("Not empty"))); } - } diff --git a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java index a3f2ce586..9b826839f 100644 --- a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java @@ -19,7 +19,7 @@ package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java index e6131cf65..83afd782d 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java @@ -23,6 +23,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.RichInputMethodSubtype; @@ -36,6 +37,7 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { private RichInputMethodManager mRichImm; private Resources mRes; + private InputMethodSubtype mSavedAddtionalSubtypes[]; RichInputMethodSubtype EN_US; RichInputMethodSubtype EN_GB; @@ -45,6 +47,8 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { RichInputMethodSubtype FR_CH; RichInputMethodSubtype DE; RichInputMethodSubtype DE_CH; + RichInputMethodSubtype HI; + RichInputMethodSubtype SR; RichInputMethodSubtype ZZ; RichInputMethodSubtype DE_QWERTY; RichInputMethodSubtype FR_QWERTZ; @@ -54,17 +58,27 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { RichInputMethodSubtype ZZ_AZERTY; RichInputMethodSubtype ZZ_PC; - // This is a preliminary subtype and may not exist. - RichInputMethodSubtype HI_LATN; + // These are preliminary subtypes and may not exist. + RichInputMethodSubtype HI_LATN; // Hinglish + RichInputMethodSubtype SR_LATN; // Serbian Latin + RichInputMethodSubtype HI_LATN_DVORAK; + RichInputMethodSubtype SR_LATN_QWERTY; @Override protected void setUp() throws Exception { super.setUp(); final Context context = getContext(); + mRes = context.getResources(); RichInputMethodManager.init(context); mRichImm = RichInputMethodManager.getInstance(); - mRes = context.getResources(); - SubtypeLocaleUtils.init(context); + + // Save and reset additional subtypes + mSavedAddtionalSubtypes = mRichImm.getAdditionalSubtypes(context); + final InputMethodSubtype[] predefinedAddtionalSubtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray( + AdditionalSubtypeUtils.createPrefSubtypes( + mRes.getStringArray(R.array.predefined_subtypes))); + mRichImm.setAdditionalInputMethodSubtypes(predefinedAddtionalSubtypes); final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme(); final int subtypeCount = imi.getSubtypeCount(); @@ -89,6 +103,10 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { Locale.GERMAN.toString(), "qwertz")); DE_CH = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( "de_CH", "swiss")); + HI = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "hi", "hindi")); + SR = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "sr", "south_slavic")); ZZ = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty")); DE_QWERTY = new RichInputMethodSubtype( @@ -117,9 +135,27 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { "hi_ZZ", "qwerty"); if (hiLatn != null) { HI_LATN = new RichInputMethodSubtype(hiLatn); + HI_LATN_DVORAK = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "hi_ZZ", "dvorak")); + } + final InputMethodSubtype srLatn = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "sr_ZZ", "serbian_qwertz"); + if (srLatn != null) { + SR_LATN = new RichInputMethodSubtype(srLatn); + SR_LATN_QWERTY = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "sr_ZZ", "qwerty")); } } + @Override + protected void tearDown() throws Exception { + // Restore additional subtypes. + mRichImm.setAdditionalInputMethodSubtypes(mSavedAddtionalSubtypes); + super.tearDown(); + } + public void testAllFullDisplayNameForSpacebar() { for (final RichInputMethodSubtype subtype : mSubtypesList) { final String subtypeName = SubtypeLocaleUtils @@ -150,10 +186,11 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { continue; } final Locale locale = locales[0]; - if (SubtypeLocaleUtils.sExceptionalLocaleDisplayedInRootLocale.contains( - locale.toString())) { + final Locale displayLocale = SubtypeLocaleUtils.getDisplayLocaleOfSubtypeLocale( + locale.toString()); + if (Locale.ROOT.equals(displayLocale)) { // Skip test because the language part of this locale string doesn't represent - // the locale to be displayed on the spacebar (for example hi_ZZ and Hinglish). + // the locale to be displayed on the spacebar (for example Hinglish). continue; } final String spacebarText = subtype.getMiddleDisplayName(); @@ -162,30 +199,36 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { subtype.getRawSubtype()), spacebarText); } else { assertEquals(subtypeName, - SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()), + SubtypeLocaleUtils.getSubtypeLanguageDisplayName(locale.toString()), spacebarText); } } } // InputMethodSubtype's display name for spacebar text in its locale. - // isAdditionalSubtype (T=true, F=false) - // locale layout | Middle Full - // ------ ------- - --------- ---------------------- - // en_US qwerty F English English (US) exception - // en_GB qwerty F English English (UK) exception - // es_US spanish F Español Español (EE.UU.) exception - // fr azerty F Français Français - // fr_CA qwerty F Français Français (Canada) - // fr_CH swiss F Français Français (Suisse) - // de qwertz F Deutsch Deutsch - // de_CH swiss F Deutsch Deutsch (Schweiz) - // hi_ZZ qwerty F Hinglish Hinglish - // zz qwerty F QWERTY QWERTY - // fr qwertz T Français Français - // de qwerty T Deutsch Deutsch - // en_US azerty T English English (US) - // zz azerty T AZERTY AZERTY + // isAdditionalSubtype (T=true, F=false) + // locale layout | Middle Full + // ------ -------------- - --------- ---------------------- + // en_US qwerty F English English (US) exception + // en_GB qwerty F English English (UK) exception + // es_US spanish F Español Español (EE.UU.) exception + // fr azerty F Français Français + // fr_CA qwerty F Français Français (Canada) + // fr_CH swiss F Français Français (Suisse) + // de qwertz F Deutsch Deutsch + // de_CH swiss F Deutsch Deutsch (Schweiz) + // hi hindi F हिन्दी हिन्दी + // hi_ZZ qwerty F Hinglish Hinglish exception + // sr south_slavic F Српски Српски + // sr_ZZ serbian_qwertz F Srpski Srpski exception + // zz qwerty F QWERTY QWERTY + // fr qwertz T Français Français + // de qwerty T Deutsch Deutsch + // en_US azerty T English English (US) + // en_GB dvorak T English English (UK) + // hi_ZZ dvorak T Hinglish Hinglish exception + // sr_ZZ qwerty T Srpski Srpski exception + // zz azerty T AZERTY AZERTY private final RunInLocale<Void> testsPredefinedSubtypesForSpacebar = new RunInLocale<Void>() { @Override @@ -198,11 +241,9 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { assertEquals("fr_CH", "Français (Suisse)", FR_CH.getFullDisplayName()); assertEquals("de", "Deutsch", DE.getFullDisplayName()); assertEquals("de_CH", "Deutsch (Schweiz)", DE_CH.getFullDisplayName()); + assertEquals("hi", "हिन्दी", HI.getFullDisplayName()); + assertEquals("sr", "Српски", SR.getFullDisplayName()); assertEquals("zz", "QWERTY", ZZ.getFullDisplayName()); - // This is a preliminary subtype and may not exist. - if (HI_LATN != null) { - assertEquals("hi_ZZ", "Hinglish", HI_LATN.getFullDisplayName()); - } assertEquals("en_US", "English", EN_US.getMiddleDisplayName()); assertEquals("en_GB", "English", EN_GB.getMiddleDisplayName()); @@ -213,10 +254,16 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { assertEquals("de", "Deutsch", DE.getMiddleDisplayName()); assertEquals("de_CH", "Deutsch", DE_CH.getMiddleDisplayName()); assertEquals("zz", "QWERTY", ZZ.getMiddleDisplayName()); - // This is a preliminary subtype and may not exist. + + // These are preliminary subtypes and may not exist. if (HI_LATN != null) { + assertEquals("hi_ZZ", "Hinglish", HI_LATN.getFullDisplayName()); assertEquals("hi_ZZ", "Hinglish", HI_LATN.getMiddleDisplayName()); } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "Srpski", SR_LATN.getFullDisplayName()); + assertEquals("sr_ZZ", "Srpski", SR_LATN.getMiddleDisplayName()); + } return null; } }; @@ -239,6 +286,16 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase { assertEquals("es_US colemak", "Español", ES_US_COLEMAK.getMiddleDisplayName()); assertEquals("zz azerty", "AZERTY", ZZ_AZERTY.getMiddleDisplayName()); assertEquals("zz pc", "PC", ZZ_PC.getMiddleDisplayName()); + + // These are preliminary subtypes and may not exist. + if (HI_LATN_DVORAK != null) { + assertEquals("hi_ZZ dvorak", "Hinglish", HI_LATN_DVORAK.getFullDisplayName()); + assertEquals("hi_ZZ dvorak", "Hinglish", HI_LATN_DVORAK.getMiddleDisplayName()); + } + if (SR_LATN_QWERTY != null) { + assertEquals("sr_ZZ qwerty", "Srpski", SR_LATN_QWERTY.getFullDisplayName()); + assertEquals("sr_ZZ qwerty", "Srpski", SR_LATN_QWERTY.getMiddleDisplayName()); + } return null; } }; diff --git a/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java index fa6ad16c1..11d10aa2f 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java @@ -21,7 +21,6 @@ import android.test.suitebuilder.annotation.SmallTest; import android.text.style.SuggestionSpan; import android.text.style.URLSpan; import android.text.SpannableStringBuilder; -import android.text.Spannable; import android.text.Spanned; @SmallTest @@ -34,8 +33,8 @@ public class SpannableStringUtilsTests extends AndroidTestCase { for (int i = 0; i < N; ++i) { // Put a PARAGRAPH-flagged span that should not be found in the result. s.setSpan(new SuggestionSpan(getContext(), - new String[] {"" + i}, Spannable.SPAN_PARAGRAPH), - i * 12, i * 12 + 12, Spannable.SPAN_PARAGRAPH); + new String[] {"" + i}, Spanned.SPAN_PARAGRAPH), + i * 12, i * 12 + 12, Spanned.SPAN_PARAGRAPH); // Put a normal suggestion span that should be found in the result. s.setSpan(new SuggestionSpan(getContext(), new String[] {"" + i}, 0), i, i * 2, 0); // Put a URL span than should not be found in the result. @@ -51,7 +50,7 @@ public class SpannableStringUtilsTests extends AndroidTestCase { for (int i = 0; i < spans.length; i++) { final int flags = result.getSpanFlags(spans[i]); assertEquals("Should not find a span with PARAGRAPH flag", - flags & Spannable.SPAN_PARAGRAPH, 0); + flags & Spanned.SPAN_PARAGRAPH, 0); assertTrue("Should be a SuggestionSpan", spans[i] instanceof SuggestionSpan); } } diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java index 637ae10ee..0389fefb0 100644 --- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java @@ -22,7 +22,9 @@ import android.text.SpannableString; import android.text.Spanned; import android.text.SpannedString; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; +import com.android.inputmethod.latin.utils.SpannableStringUtils; import java.util.Arrays; import java.util.List; @@ -375,9 +377,9 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { spannableString.setSpan(span1, 0, 7, SPAN1_FLAGS); spannableString.setSpan(span2, 0, 5, SPAN2_FLAGS); spannableString.setSpan(span3, 12, 13, SPAN3_FLAGS); - final CharSequence[] charSequencesFromSpanned = StringUtils.split( + final CharSequence[] charSequencesFromSpanned = SpannableStringUtils.split( spannableString, " ", true /* preserveTrailingEmptySegmengs */); - final CharSequence[] charSequencesFromString = StringUtils.split( + final CharSequence[] charSequencesFromString = SpannableStringUtils.split( spannableString.toString(), " ", true /* preserveTrailingEmptySegmengs */); @@ -456,44 +458,44 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { } public void testSplitCharSequencePreserveTrailingEmptySegmengs() { - assertEquals(1, StringUtils.split("", " ", + assertEquals(1, SpannableStringUtils.split("", " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(1, StringUtils.split(new SpannedString(""), " ", + assertEquals(1, SpannableStringUtils.split(new SpannedString(""), " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(1, StringUtils.split("", " ", + assertEquals(1, SpannableStringUtils.split("", " ", true /* preserveTrailingEmptySegmengs */).length); - assertEquals(1, StringUtils.split(new SpannedString(""), " ", + assertEquals(1, SpannableStringUtils.split(new SpannedString(""), " ", true /* preserveTrailingEmptySegmengs */).length); - assertEquals(0, StringUtils.split(" ", " ", + assertEquals(0, SpannableStringUtils.split(" ", " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(0, StringUtils.split(new SpannedString(" "), " ", + assertEquals(0, SpannableStringUtils.split(new SpannedString(" "), " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(2, StringUtils.split(" ", " ", + assertEquals(2, SpannableStringUtils.split(" ", " ", true /* preserveTrailingEmptySegmengs */).length); - assertEquals(2, StringUtils.split(new SpannedString(" "), " ", + assertEquals(2, SpannableStringUtils.split(new SpannedString(" "), " ", true /* preserveTrailingEmptySegmengs */).length); - assertEquals(3, StringUtils.split("a b c ", " ", + assertEquals(3, SpannableStringUtils.split("a b c ", " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(3, StringUtils.split(new SpannedString("a b c "), " ", + assertEquals(3, SpannableStringUtils.split(new SpannedString("a b c "), " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(5, StringUtils.split("a b c ", " ", + assertEquals(5, SpannableStringUtils.split("a b c ", " ", true /* preserveTrailingEmptySegmengs */).length); - assertEquals(5, StringUtils.split(new SpannedString("a b c "), " ", + assertEquals(5, SpannableStringUtils.split(new SpannedString("a b c "), " ", true /* preserveTrailingEmptySegmengs */).length); - assertEquals(6, StringUtils.split("a b ", " ", + assertEquals(6, SpannableStringUtils.split("a b ", " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(6, StringUtils.split(new SpannedString("a b "), " ", + assertEquals(6, SpannableStringUtils.split(new SpannedString("a b "), " ", false /* preserveTrailingEmptySegmengs */).length); - assertEquals(7, StringUtils.split("a b ", " ", + assertEquals(7, SpannableStringUtils.split("a b ", " ", true /* preserveTrailingEmptySegmengs */).length); - assertEquals(7, StringUtils.split(new SpannedString("a b "), " ", + assertEquals(7, SpannableStringUtils.split(new SpannedString("a b "), " ", true /* preserveTrailingEmptySegmengs */).length); } } diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java index dfc3fecfd..54f478f5a 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java @@ -23,6 +23,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.RichInputMethodSubtype; @@ -36,6 +37,7 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { private RichInputMethodManager mRichImm; private Resources mRes; + private InputMethodSubtype mSavedAddtionalSubtypes[]; InputMethodSubtype EN_US; InputMethodSubtype EN_GB; @@ -45,6 +47,8 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { InputMethodSubtype FR_CH; InputMethodSubtype DE; InputMethodSubtype DE_CH; + InputMethodSubtype HI; + InputMethodSubtype SR; InputMethodSubtype ZZ; InputMethodSubtype DE_QWERTY; InputMethodSubtype FR_QWERTZ; @@ -54,17 +58,27 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { InputMethodSubtype ZZ_AZERTY; InputMethodSubtype ZZ_PC; - // This is a preliminary subtype and may not exist. - InputMethodSubtype HI_LATN; + // These are preliminary subtypes and may not exist. + InputMethodSubtype HI_LATN; // Hinglish + InputMethodSubtype SR_LATN; // Serbian Latin + InputMethodSubtype HI_LATN_DVORAK; // Hinglis Dvorak + InputMethodSubtype SR_LATN_QWERTY; // Serbian Latin Qwerty @Override protected void setUp() throws Exception { super.setUp(); final Context context = getContext(); + mRes = context.getResources(); RichInputMethodManager.init(context); mRichImm = RichInputMethodManager.getInstance(); - mRes = context.getResources(); - SubtypeLocaleUtils.init(context); + + // Save and reset additional subtypes + mSavedAddtionalSubtypes = mRichImm.getAdditionalSubtypes(context); + final InputMethodSubtype[] predefinedAddtionalSubtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray( + AdditionalSubtypeUtils.createPrefSubtypes( + mRes.getStringArray(R.array.predefined_subtypes))); + mRichImm.setAdditionalInputMethodSubtypes(predefinedAddtionalSubtypes); final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme(); final int subtypeCount = imi.getSubtypeCount(); @@ -89,6 +103,10 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { Locale.GERMAN.toString(), "qwertz"); DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( "de_CH", "swiss"); + HI = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "hi", "hindi"); + SR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "sr", "south_slavic"); ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( @@ -107,6 +125,22 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty"); HI_LATN = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet("hi_ZZ", "qwerty"); + if (HI_LATN != null) { + HI_LATN_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "hi_ZZ", "dvorak"); + } + SR_LATN = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet("sr_ZZ", "serbian_qwertz"); + if (SR_LATN != null) { + SR_LATN_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "sr_ZZ", "qwerty"); + } + } + + @Override + protected void tearDown() throws Exception { + // Restore additional subtypes. + mRichImm.setAdditionalInputMethodSubtypes(mSavedAddtionalSubtypes); + super.tearDown(); } public void testAllFullDisplayName() { @@ -139,11 +173,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { assertEquals("fr_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CH)); assertEquals("de", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE)); assertEquals("de_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_CH)); + assertEquals("hi", "hindi", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI)); + assertEquals("sr", "south_slavic", SubtypeLocaleUtils.getKeyboardLayoutSetName(SR)); assertEquals("zz", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ)); - // This is a preliminary subtype and may not exist. - if (HI_LATN != null) { - assertEquals("hi_ZZ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_LATN)); - } assertEquals("de qwerty", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_QWERTY)); assertEquals("fr qwertz", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_QWERTZ)); @@ -155,28 +187,46 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US_COLEMAK)); assertEquals("zz azerty", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ_AZERTY)); + + // These are preliminary subtypes and may not exist. + if (HI_LATN != null) { + assertEquals("hi_ZZ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_LATN)); + assertEquals("hi_ZZ dvorak", "dvorak", + SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_LATN_DVORAK)); + } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "serbian_qwertz", + SubtypeLocaleUtils.getKeyboardLayoutSetName(SR_LATN)); + assertEquals("sr_ZZ qwerty", "qwerty", + SubtypeLocaleUtils.getKeyboardLayoutSetName(SR_LATN_QWERTY)); + } } // InputMethodSubtype's display name in system locale (en_US). - // isAdditionalSubtype (T=true, F=false) - // locale layout | display name - // ------ ------- - ---------------------- - // en_US qwerty F English (US) exception - // en_GB qwerty F English (UK) exception - // es_US spanish F Spanish (US) exception - // fr azerty F French - // fr_CA qwerty F French (Canada) - // fr_CH swiss F French (Switzerland) - // de qwertz F German - // de_CH swiss F German (Switzerland) - // hi_ZZ qwerty F Hinglish - // zz qwerty F Alphabet (QWERTY) - // fr qwertz T French (QWERTZ) - // de qwerty T German (QWERTY) - // en_US azerty T English (US) (AZERTY) exception - // en_UK dvorak T English (UK) (Dvorak) exception - // es_US colemak T Spanish (US) (Colemak) exception - // zz pc T Alphabet (PC) + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ -------------- - ---------------------- + // en_US qwerty F English (US) exception + // en_GB qwerty F English (UK) exception + // es_US spanish F Spanish (US) exception + // fr azerty F French + // fr_CA qwerty F French (Canada) + // fr_CH swiss F French (Switzerland) + // de qwertz F German + // de_CH swiss F German (Switzerland) + // hi hindi F Hindi + // hi_ZZ qwerty F Hinglish exception + // sr south_slavic F Serbian + // sr_ZZ serbian_qwertz F Serbian (Latin) exception + // zz qwerty F Alphabet (QWERTY) + // fr qwertz T French (QWERTZ) + // de qwerty T German (QWERTY) + // en_US azerty T English (US) (AZERTY) exception + // en_UK dvorak T English (UK) (Dvorak) exception + // es_US colemak T Spanish (US) (Colemak) exception + // hi_ZZ dvorak T Hinglish (Dvorka) exception + // sr_ZZ qwerty T Serbian (QWERTY) exception + // zz pc T Alphabet (PC) public void testPredefinedSubtypesInEnglishSystemLocale() { final RunInLocale<Void> tests = new RunInLocale<Void>() { @@ -198,13 +248,21 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); assertEquals("de_CH", "German (Switzerland)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); + assertEquals("hi", "Hindi", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI)); + assertEquals("sr", "Serbian", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR)); assertEquals("zz", "Alphabet (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); - // This is a preliminary subtype and may not exist. + // These are preliminary subtypes and may not exist. if (HI_LATN != null) { assertEquals("hi_ZZ", "Hinglish", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN)); } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "Serbian (Latin)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN)); + } return null; } }; @@ -229,6 +287,15 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY)); assertEquals("zz pc", "Alphabet (PC)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); + // These are preliminary subtypes and may not exist. + if (HI_LATN_DVORAK != null) { + assertEquals("hi_ZZ", "Hinglish (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN_DVORAK)); + } + if (SR_LATN_QWERTY != null) { + assertEquals("sr_ZZ", "Serbian (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY)); + } return null; } }; @@ -239,22 +306,27 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { // isAdditionalSubtype (T=true, F=false) // locale layout | display name // ------ ------- - ---------------------- - // en_US qwerty F Anglais (États-Unis) exception - // en_GB qwerty F Anglais (Royaume-Uni) exception - // es_US spanish F Espagnol (États-Unis) exception - // fr azerty F Français - // fr_CA qwerty F Français (Canada) - // fr_CH swiss F Français (Suisse) - // de qwertz F Allemand - // de_CH swiss F Allemand (Suisse) - // hi_ZZ qwerty F Hinglish - // zz qwerty F Alphabet latin (QWERTY) - // fr qwertz T Français (QWERTZ) - // de qwerty T Allemand (QWERTY) - // en_US azerty T Anglais (États-Unis) (AZERTY) exception - // en_UK dvorak T Anglais (Royaume-Uni) (Dvorak) exception - // es_US colemak T Espagnol (États-Unis) (Colemak) exception - // zz pc T Alphabet latin (PC) + // en_US qwerty F Anglais (États-Unis) exception + // en_GB qwerty F Anglais (Royaume-Uni) exception + // es_US spanish F Espagnol (États-Unis) exception + // fr azerty F Français + // fr_CA qwerty F Français (Canada) + // fr_CH swiss F Français (Suisse) + // de qwertz F Allemand + // de_CH swiss F Allemand (Suisse) + // hi hindi F Hindi exception + // hi_ZZ qwerty F Hindi/Anglais exception + // sr south_slavic F Serbe exception + // sr_ZZ serbian_qwertz F Serbe (latin) exception + // zz qwerty F Alphabet latin (QWERTY) + // fr qwertz T Français (QWERTZ) + // de qwerty T Allemand (QWERTY) + // en_US azerty T Anglais (États-Unis) (AZERTY) exception + // en_UK dvorak T Anglais (Royaume-Uni) (Dvorak) exception + // es_US colemak T Espagnol (États-Unis) (Colemak) exception + // hi_ZZ dvorak T Hindi/Anglais (Dvorka) exception + // sr_ZZ qwerty T Serbe (QWERTY) exception + // zz pc T Alphabet latin (PC) public void testPredefinedSubtypesInFrenchSystemLocale() { final RunInLocale<Void> tests = new RunInLocale<Void>() { @@ -276,13 +348,21 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); assertEquals("de_CH", "Allemand (Suisse)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); + assertEquals("hi", "Hindi", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI)); + assertEquals("sr", "Serbe", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR)); assertEquals("zz", "Alphabet latin (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); - // This is a preliminary subtype and may not exist. + // These are preliminary subtypes and may not exist. if (HI_LATN != null) { assertEquals("hi_ZZ", "Hindi/Anglais", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN)); } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "Serbe (latin)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN)); + } return null; } }; @@ -307,12 +387,77 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY)); assertEquals("zz pc", "Alphabet latin (PC)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); + // These are preliminary subtypes and may not exist. + if (HI_LATN_DVORAK != null) { + assertEquals("hi_ZZ", "Hindi/Anglais (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN_DVORAK)); + } + if (SR_LATN_QWERTY != null) { + assertEquals("sr_ZZ", "Serbe (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY)); + } return null; } }; tests.runInLocale(mRes, Locale.FRENCH); } + // InputMethodSubtype's display name in system locale (hi). + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ ------- - ---------------------- + // hi hindi F हिन्दी + // hi_ZZ qwerty F हिंग्लिश + // hi_ZZ dvorak T हिंग्लिश (Dvorak) + + public void testHinglishSubtypesInHindiSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job (final Resources res) { + assertEquals("hi", "हिन्दी", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI)); + // These are preliminary subtypes and may not exist. + if (HI_LATN != null) { + assertEquals("hi_ZZ", "हिंग्लिश", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN)); + assertEquals("hi_ZZ", "हिंग्लिश (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN_DVORAK)); + } + return null; + } + }; + tests.runInLocale(mRes, new Locale("hi")); + } + + // InputMethodSubtype's display name in system locale (sr). + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ -------------- - ---------------------- + // sr south_slavic F Српски + // sr_ZZ serbian_qwertz F српски (латиница) + // sr_ZZ qwerty T српски (QWERTY) + + public void testSerbianLatinSubtypesInSerbianSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job (final Resources res) { + assertEquals("sr", "Српски", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR)); + // These are preliminary subtypes and may not exist. + if (SR_LATN != null) { + // TODO: Uncommented because of the current translation of these strings + // in Seriban are described in Latin script. +// assertEquals("sr_ZZ", "српски (латиница)", +// SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN)); +// assertEquals("sr_ZZ", "српски (QWERTY)", +// SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY)); + } + return null; + } + }; + tests.runInLocale(mRes, new Locale("sr")); + } + public void testIsRtlLanguage() { // Known Right-to-Left language subtypes. final InputMethodSubtype ARABIC = mRichImm diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index 7f34ccf20..42659253a 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -35,7 +35,6 @@ LATINIME_LOCAL_DIR := ../.. LATINIME_BASE_SRC_DIR := $(LATINIME_LOCAL_DIR)/java/src/com/android/inputmethod LATINIME_BASE_OVERRIDABLE_SRC_DIR := \ $(LATINIME_LOCAL_DIR)/java-overridable/src/com/android/inputmethod -LATINIME_ANNOTATIONS_SRC_DIR := $(LATINIME_BASE_SRC_DIR)/annotations MAKEDICT_CORE_SRC_DIR := $(LATINIME_BASE_SRC_DIR)/latin/makedict LATINIME_TESTS_SRC_DIR := $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin @@ -43,16 +42,11 @@ LATINIME_TESTS_SRC_DIR := $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmetho # a significant part of the dependencies are mocked in the compat/ directory, with empty or # nearly-empty implementations, for parts that we don't use in Dicttool. LATINIME_SRC_FILES_FOR_DICTTOOL := \ - event/Combiner.java \ - event/Event.java \ latin/BinaryDictionary.java \ latin/DicTraverseSession.java \ latin/Dictionary.java \ - latin/InputPointers.java \ - latin/LastComposedWord.java \ latin/NgramContext.java \ latin/SuggestedWords.java \ - latin/WordComposer.java \ latin/settings/NativeSuggestOptions.java \ latin/settings/SettingsValuesForSuggestion.java \ latin/utils/BinaryDictionaryUtils.java \ @@ -60,9 +54,7 @@ LATINIME_SRC_FILES_FOR_DICTTOOL := \ latin/utils/CoordinateUtils.java \ latin/utils/FileUtils.java \ latin/utils/JniUtils.java \ - latin/utils/LocaleUtils.java \ - latin/utils/ResizableIntArray.java \ - latin/utils/StringUtils.java + latin/utils/LocaleUtils.java LATINIME_OVERRIDABLE_SRC_FILES_FOR_DICTTOOL := \ latin/define/DebugFlags.java @@ -82,18 +74,15 @@ DICTTOOL_COMPAT_TESTS_DIR := compat LOCAL_MAIN_SRC_FILES := $(call all-java-files-under, $(MAKEDICT_CORE_SRC_DIR)) LOCAL_TOOL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_ANNOTATIONS_SRC_FILES := $(call all-java-files-under, $(LATINIME_ANNOTATIONS_SRC_DIR)) LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \ $(filter-out $(addprefix %/, $(notdir $(LOCAL_TOOL_SRC_FILES))), $(LOCAL_MAIN_SRC_FILES)) \ - $(call all-java-files-under, $(DICTTOOL_COMPAT_TESTS_DIR)) \ - $(LOCAL_ANNOTATIONS_SRC_FILES) $(USED_TARGETED_SRC_FILES) \ - $(LATINIME_BASE_SRC_DIR)/latin/Constants.java \ - $(call all-java-files-under, tests) \ - $(call all-java-files-under, $(DICTTOOL_ONDEVICE_TESTS_DIR)) + $(USED_TARGETED_SRC_FILES) \ + $(call all-java-files-under, \ + tests $(DICTTOOL_COMPAT_TESTS_DIR) $(DICTTOOL_ONDEVICE_TESTS_DIR)) LOCAL_JAVA_LIBRARIES := junit -LOCAL_STATIC_JAVA_LIBRARIES := jsr305lib +LOCAL_STATIC_JAVA_LIBRARIES := jsr305lib latinime-common-host LOCAL_REQUIRED_MODULES := $(LATINIME_HOST_NATIVE_LIBNAME) LOCAL_JAR_MANIFEST := etc/manifest.txt LOCAL_MODULE := dicttool_aosp diff --git a/tools/dicttool/compat/android/test/MoreAsserts.java b/tools/dicttool/compat/android/test/MoreAsserts.java deleted file mode 100644 index f56420b9c..000000000 --- a/tools/dicttool/compat/android/test/MoreAsserts.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 android.test; - -import junit.framework.Assert; - -/** - * This is a compatibility class that aims at emulating android.test.MoreAsserts from the - * Android library as simply as possible, and only to the extent that is used by the client classes. - * Its purpose is to provide compatibility without having to pull the whole Android library. - */ -public class MoreAsserts { - public static void assertNotEqual(Object unexpected, Object actual) { - if (equal(unexpected, actual)) { - Assert.fail("expected not to be:<" + unexpected + ">"); - } - } - private static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } -} diff --git a/tools/dicttool/compat/android/text/TextUtils.java b/tools/dicttool/compat/android/text/TextUtils.java index 5a94b7d4c..82483319e 100644 --- a/tools/dicttool/compat/android/text/TextUtils.java +++ b/tools/dicttool/compat/android/text/TextUtils.java @@ -25,10 +25,7 @@ public class TextUtils { * @return true if str is null or zero length */ public static boolean isEmpty(CharSequence str) { - if (str == null || str.length() == 0) - return true; - else - return false; + return (str == null || str.length() == 0); } /** @@ -45,12 +42,11 @@ public class TextUtils { if (a != null && b != null && (length = a.length()) == b.length()) { if (a instanceof String && b instanceof String) { return a.equals(b); - } else { - for (int i = 0; i < length; i++) { - if (a.charAt(i) != b.charAt(i)) return false; - } - return true; } + for (int i = 0; i < length; i++) { + if (a.charAt(i) != b.charAt(i)) return false; + } + return true; } return false; } @@ -90,7 +86,7 @@ public class TextUtils { * @param tokens an array objects to be joined. Strings will be formed from * the objects by calling object.toString(). */ - public static String join(CharSequence delimiter, Iterable tokens) { + public static String join(CharSequence delimiter, Iterable<?> tokens) { StringBuilder sb = new StringBuilder(); boolean firstTime = true; for (Object token: tokens) { diff --git a/tools/dicttool/compat/android/util/Pair.java b/tools/dicttool/compat/android/util/Pair.java index ab6096ee3..e61e896b7 100644 --- a/tools/dicttool/compat/android/util/Pair.java +++ b/tools/dicttool/compat/android/util/Pair.java @@ -16,7 +16,6 @@ package android.util; -import java.util.Arrays; import java.util.Objects; public class Pair<T1, T2> { diff --git a/tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java b/tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java index fbce72556..e2f769ec8 100644 --- a/tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java +++ b/tools/dicttool/compat/android/view/inputmethod/CompletionInfo.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +@SuppressWarnings("static-method") public class CompletionInfo { public final String getText() { return ""; } } diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java deleted file mode 100644 index c4457a1b7..000000000 --- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.event; - -import java.util.ArrayList; - -/** - * Compatibility class that stands in for the combiner chain in LatinIME. - * - * This is not used by dicttool, it's just needed by the dependency chain. - */ -// TODO: there should not be a dependency to this in dicttool, so there -// should be a sensible way to separate them cleanly. -public class CombinerChain { - private StringBuilder mComposingWord; - public CombinerChain(final String initialText, final Combiner... combinerList) { - mComposingWord = new StringBuilder(initialText); - } - - public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { - return newEvent; - } - - public void applyProcessedEvent(final Event event) { - mComposingWord.append(event.getTextToCommit()); - } - - public CharSequence getComposingWordWithCombiningFeedback() { - return mComposingWord; - } - - public void reset() { - mComposingWord.setLength(0); - } - - public static Combiner[] createCombiners(final String spec) { - // Dicttool never uses a combiner at all, so we just return a zero-sized array. - return new Combiner[0]; - } -} diff --git a/tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java b/tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java index 1e63bb526..925940650 100644 --- a/tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java +++ b/tools/dicttool/compat/com/android/inputmethod/keyboard/Key.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +@SuppressWarnings("static-method") public class Key { public final int getX() { return 0; } public final int getY() { return 0; } diff --git a/tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java b/tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java index 61b209f4d..3d6bfd0a9 100644 --- a/tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java +++ b/tools/dicttool/compat/com/android/inputmethod/keyboard/Keyboard.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +@SuppressWarnings("unused") public class Keyboard { private final Key KEY = new Key(); public final Key getKey(final int i) { return KEY; } diff --git a/tools/dicttool/compat/com/android/inputmethod/latin/utils/LanguageModelParam.java b/tools/dicttool/compat/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java index f4ca94a81..b5a729421 100644 --- a/tools/dicttool/compat/com/android/inputmethod/latin/utils/LanguageModelParam.java +++ b/tools/dicttool/compat/com/android/inputmethod/latin/utils/WordInputEventForPersonalization.java @@ -16,5 +16,5 @@ package com.android.inputmethod.latin.utils; -public final class LanguageModelParam { +public final class WordInputEventForPersonalization { } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java index 3ef03f4bd..3ec28f313 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java @@ -19,24 +19,30 @@ package com.android.inputmethod.latin.dicttool; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.DictDecoder; +import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; -import org.xml.sax.SAXException; - import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.util.Arrays; import java.util.ArrayList; +import java.util.HashMap; -import javax.xml.parsers.ParserConfigurationException; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * Class grouping utilities for offline dictionary making. @@ -48,126 +54,201 @@ public final class BinaryDictOffdeviceUtils { // Prefix and suffix are arbitrary, the values do not really matter private final static String PREFIX = "dicttool"; private final static String SUFFIX = ".tmp"; + private final static int COPY_BUFFER_SIZE = 8192; - public final static String COMPRESSION = "compressed"; - public final static String ENCRYPTION = "encrypted"; + public static class DecoderChainSpec<T> { + public final static int COMPRESSION = 1; + public final static int ENCRYPTION = 2; - private final static int MAX_DECODE_DEPTH = 8; - private final static int COPY_BUFFER_SIZE = 8192; + private final static int[][] VALID_DECODER_CHAINS = { + { }, { COMPRESSION }, { ENCRYPTION, COMPRESSION } + }; + + private final int mDecoderSpecIndex; + public T mResult; - public static class DecoderChainSpec { - ArrayList<String> mDecoderSpec = new ArrayList<>(); - File mFile; + public DecoderChainSpec() { + mDecoderSpecIndex = 0; + mResult = null; + } + + private DecoderChainSpec(final DecoderChainSpec<T> src) { + mDecoderSpecIndex = src.mDecoderSpecIndex + 1; + mResult = src.mResult; + } - public DecoderChainSpec addStep(final String stepDescription) { - mDecoderSpec.add(stepDescription); - return this; + private String getStepDescription(final int step) { + switch (step) { + case COMPRESSION: + return "compression"; + case ENCRYPTION: + return "encryption"; + default: + return "unknown"; + } } public String describeChain() { final StringBuilder s = new StringBuilder("raw"); - for (final String step : mDecoderSpec) { + for (final int step : VALID_DECODER_CHAINS[mDecoderSpecIndex]) { s.append(" > "); - s.append(step); + s.append(getStepDescription(step)); } return s.toString(); } - } - public static void copy(final InputStream input, final OutputStream output) throws IOException { - final byte[] buffer = new byte[COPY_BUFFER_SIZE]; - for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) { - output.write(buffer, 0, readBytes); + /** + * Returns the next sequential spec. If exhausted, return null. + */ + public DecoderChainSpec next() { + if (mDecoderSpecIndex + 1 >= VALID_DECODER_CHAINS.length) { + return null; + } + return new DecoderChainSpec(this); } - } - /** - * Returns a decrypted/uncompressed dictionary. - * - * This will decrypt/uncompress any number of times as necessary until it finds the - * dictionary signature, and copy the decoded file to a temporary place. - * If this is not a dictionary, the method returns null. - */ - public static DecoderChainSpec getRawDictionaryOrNull(final File src) { - return getRawDictionaryOrNullInternal(new DecoderChainSpec(), src, 0); + public InputStream getStream(final File src) throws FileNotFoundException, IOException { + InputStream input = new BufferedInputStream(new FileInputStream(src)); + for (final int step : VALID_DECODER_CHAINS[mDecoderSpecIndex]) { + switch (step) { + case COMPRESSION: + input = Compress.getUncompressedStream(input); + break; + case ENCRYPTION: + input = Crypt.getDecryptedStream(input); + break; + } + } + return input; + } } - private static DecoderChainSpec getRawDictionaryOrNullInternal( - final DecoderChainSpec spec, final File src, final int depth) { - // Unfortunately the decoding scheme we use can consider any data to be encrypted - // and will product some output, meaning it's not possible to reliably detect encrypted - // data. Thus, some non-dictionary files (especially small) ones may successfully decrypt - // over and over, ending in a stack overflow. Hence we limit the depth at which we try - // decoding the file. - if (depth > MAX_DECODE_DEPTH) return null; - if (BinaryDictDecoderUtils.isBinaryDictionary(src) - || CombinedInputOutput.isCombinedDictionary(src.getAbsolutePath())) { - spec.mFile = src; - return spec; - } - // It's not a raw dictionary - try to see if it's compressed. - final File uncompressedFile = tryGetUncompressedFile(src); - if (null != uncompressedFile) { - final DecoderChainSpec newSpec = - getRawDictionaryOrNullInternal(spec, uncompressedFile, depth + 1); - if (null == newSpec) return null; - return newSpec.addStep(COMPRESSION); - } - // It's not a compressed either - try to see if it's crypted. - final File decryptedFile = tryGetDecryptedFile(src); - if (null != decryptedFile) { - final DecoderChainSpec newSpec = - getRawDictionaryOrNullInternal(spec, decryptedFile, depth + 1); - if (null == newSpec) return null; - return newSpec.addStep(ENCRYPTION); - } - return null; + public interface InputProcessor<T> { + @Nonnull + public T process(@Nonnull final InputStream input) + throws IOException, UnsupportedFormatException; } - /* Try to uncompress the file passed as an argument. - * - * If the file can be uncompressed, the uncompressed version is returned. Otherwise, null - * is returned. - */ - private static File tryGetUncompressedFile(final File src) { - try { + public static class CopyProcessor implements InputProcessor<File> { + @Override @Nonnull + public File process(@Nonnull final InputStream input) throws IOException, + UnsupportedFormatException { final File dst = File.createTempFile(PREFIX, SUFFIX); dst.deleteOnExit(); - try ( - final InputStream input = Compress.getUncompressedStream( - new BufferedInputStream(new FileInputStream(src))); - final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst)) - ) { + try (final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst))) { copy(input, output); - return dst; + output.flush(); + output.close(); + if (BinaryDictDecoderUtils.isBinaryDictionary(dst) + || CombinedInputOutput.isCombinedDictionary(dst.getAbsolutePath())) { + return dst; + } } - } catch (final IOException e) { - // Could not uncompress the file: presumably the file is simply not a compressed file - return null; + throw new UnsupportedFormatException("Input stream not at the expected format"); } } - /* Try to decrypt the file passed as an argument. + public static class HeaderReaderProcessor implements InputProcessor<DictionaryHeader> { + // Arbitrarily limit the header length to 32k. Sounds like it would never be larger + // than this. Revisit this if needed later. + private final int MAX_HEADER_LENGTH = 32 * 1024; + @Override @Nonnull + public DictionaryHeader process(final InputStream input) throws IOException, + UnsupportedFormatException { + // Do everything as curtly and ad-hoc as possible for performance. + final byte[] tmpBuffer = new byte[12]; + if (tmpBuffer.length != input.read(tmpBuffer)) { + throw new UnsupportedFormatException("File too short, not a dictionary"); + } + // Ad-hoc check for the magic number. See FormatSpec.java as well as + // byte_array_utils.h and BinaryDictEncoderUtils#writeDictionaryHeader(). + final int MAGIC_NUMBER_START_OFFSET = 0; + final int VERSION_START_OFFSET = 4; + final int HEADER_SIZE_OFFSET = 8; + final int magicNumber = ((tmpBuffer[MAGIC_NUMBER_START_OFFSET] & 0xFF) << 24) + + ((tmpBuffer[MAGIC_NUMBER_START_OFFSET + 1] & 0xFF) << 16) + + ((tmpBuffer[MAGIC_NUMBER_START_OFFSET + 2] & 0xFF) << 8) + + (tmpBuffer[MAGIC_NUMBER_START_OFFSET + 3] & 0xFF); + if (magicNumber != FormatSpec.MAGIC_NUMBER) { + throw new UnsupportedFormatException("Wrong magic number"); + } + final int version = ((tmpBuffer[VERSION_START_OFFSET] & 0xFF) << 8) + + (tmpBuffer[VERSION_START_OFFSET + 1] & 0xFF); + if (version != FormatSpec.VERSION2 && version != FormatSpec.VERSION201 + && version != FormatSpec.VERSION202) { + throw new UnsupportedFormatException("Only versions 2, 201, 202 are supported"); + } + final int totalHeaderSize = ((tmpBuffer[HEADER_SIZE_OFFSET] & 0xFF) << 24) + + ((tmpBuffer[HEADER_SIZE_OFFSET + 1] & 0xFF) << 16) + + ((tmpBuffer[HEADER_SIZE_OFFSET + 2] & 0xFF) << 8) + + (tmpBuffer[HEADER_SIZE_OFFSET + 3] & 0xFF); + if (totalHeaderSize > MAX_HEADER_LENGTH) { + throw new UnsupportedFormatException("Header too large"); + } + final byte[] headerBuffer = new byte[totalHeaderSize - tmpBuffer.length]; + if (headerBuffer.length != input.read(headerBuffer)) { + throw new UnsupportedFormatException("File shorter than specified in the header"); + } + final HashMap<String, String> attributes = + BinaryDictDecoderUtils.decodeHeaderAttributes(headerBuffer); + return new DictionaryHeader(totalHeaderSize, new DictionaryOptions(attributes), + new FormatOptions(version, false /* hasTimestamp */)); + } + } + + public static void copy(final InputStream input, final OutputStream output) throws IOException { + final byte[] buffer = new byte[COPY_BUFFER_SIZE]; + for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) { + output.write(buffer, 0, readBytes); + } + } + + /** + * Process a dictionary, decrypting/uncompressing it on the fly as necessary. * - * If the file can be decrypted, the decrypted version is returned. Otherwise, null - * is returned. + * This will execute the given processor repeatedly with the possible alternatives + * for dictionary format until the processor does not throw an exception. + * If the processor succeeds for none of the possible formats, the method returns null. */ - private static File tryGetDecryptedFile(final File src) { - try { - final File dst = File.createTempFile(PREFIX, SUFFIX); - dst.deleteOnExit(); - try ( - final InputStream input = Crypt.getDecryptedStream( - new BufferedInputStream(new FileInputStream(src))); - final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst)) - ) { - copy(input, output); - return dst; + @Nullable + public static <T> DecoderChainSpec<T> decodeDictionaryForProcess(@Nonnull final File src, + @Nonnull final InputProcessor<T> processor) { + @Nonnull DecoderChainSpec spec = new DecoderChainSpec(); + while (null != spec) { + try { + final InputStream input = spec.getStream(src); + spec.mResult = processor.process(input); + try { + input.close(); + } catch (IOException e) { + // CipherInputStream doesn't like being closed without having read the + // entire stream, for some reason. But we don't want to because it's a waste + // of resources. We really, really don't care about this. + // However on close() CipherInputStream does throw this exception, wrapped + // in an IOException so we need to catch it. + if (!(e.getCause() instanceof javax.crypto.BadPaddingException)) { + throw e; + } + } + return spec; + } catch (IOException | UnsupportedFormatException | ArrayIndexOutOfBoundsException e) { + // If the format is not the right one for this file, the processor will throw one + // of these exceptions. In our case, that means we should try the next spec, + // since it may still be at another format we haven't tried yet. + // TODO: stop using exceptions for this non-exceptional case. } - } catch (final IOException e) { - // Could not decrypt the file: presumably the file is simply not a crypted file - return null; + spec = spec.next(); } + return null; + } + + /** + * Get a decoder chain spec with a raw dictionary file. This makes a new file on the + * disk ready for any treatment the client wants. + */ + @Nullable + public static DecoderChainSpec<File> getRawDictionaryOrNull(@Nonnull final File src) { + return decodeDictionaryForProcess(src, new CopyProcessor()); } static FusionDictionary getDictionary(final String filename, final boolean report) { @@ -177,40 +258,31 @@ public final class BinaryDictOffdeviceUtils { System.out.println("Size : " + file.length() + " bytes"); } try { - if (XmlDictInputOutput.isXmlUnigramDictionary(filename)) { - if (report) { - System.out.println("Format : XML unigram list"); - } - return XmlDictInputOutput.readDictionaryXml( - new BufferedInputStream(new FileInputStream(file)), - null /* shortcuts */, null /* bigrams */); - } - final DecoderChainSpec decodedSpec = getRawDictionaryOrNull(file); + final DecoderChainSpec<File> decodedSpec = getRawDictionaryOrNull(file); if (null == decodedSpec) { throw new RuntimeException("Does not seem to be a dictionary file " + filename); } - if (CombinedInputOutput.isCombinedDictionary(decodedSpec.mFile.getAbsolutePath())) { + if (CombinedInputOutput.isCombinedDictionary(decodedSpec.mResult.getAbsolutePath())) { if (report) { System.out.println("Format : Combined format"); System.out.println("Packaging : " + decodedSpec.describeChain()); - System.out.println("Uncompressed size : " + decodedSpec.mFile.length()); + System.out.println("Uncompressed size : " + decodedSpec.mResult.length()); } try (final BufferedReader reader = new BufferedReader( - new InputStreamReader(new FileInputStream(decodedSpec.mFile), "UTF-8"))) { + new InputStreamReader(new FileInputStream(decodedSpec.mResult), "UTF-8"))) { return CombinedInputOutput.readDictionaryCombined(reader); } } final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder( - decodedSpec.mFile, 0, decodedSpec.mFile.length(), + decodedSpec.mResult, 0, decodedSpec.mResult.length(), DictDecoder.USE_BYTEARRAY); if (report) { System.out.println("Format : Binary dictionary format"); System.out.println("Packaging : " + decodedSpec.describeChain()); - System.out.println("Uncompressed size : " + decodedSpec.mFile.length()); + System.out.println("Uncompressed size : " + decodedSpec.mResult.length()); } return dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */); - } catch (final IOException | SAXException | ParserConfigurationException | - UnsupportedFormatException e) { + } catch (final IOException | UnsupportedFormatException e) { throw new RuntimeException("Can't read file " + filename, e); } } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java index 23cbee81c..955c5728c 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java @@ -98,6 +98,7 @@ public class CombinedInputOutput { String word = null; ProbabilityInfo probabilityInfo = new ProbabilityInfo(0); boolean isNotAWord = false; + boolean isPossiblyOffensive = false; ArrayList<WeightedString> bigrams = new ArrayList<>(); ArrayList<WeightedString> shortcuts = new ArrayList<>(); while (null != (line = reader.readLine())) { @@ -106,7 +107,7 @@ public class CombinedInputOutput { if (args[0].matches(CombinedFormatUtils.WORD_TAG + "=.*")) { if (null != word) { dict.add(word, probabilityInfo, shortcuts.isEmpty() ? null : shortcuts, - isNotAWord); + isNotAWord, isPossiblyOffensive); for (WeightedString s : bigrams) { dict.setBigram(word, s.mWord, s.mProbabilityInfo); } @@ -114,27 +115,37 @@ public class CombinedInputOutput { if (!shortcuts.isEmpty()) shortcuts = new ArrayList<>(); if (!bigrams.isEmpty()) bigrams = new ArrayList<>(); isNotAWord = false; + isPossiblyOffensive = false; for (String param : args) { final String params[] = param.split("=", 2); if (2 != params.length) throw new RuntimeException("Wrong format : " + line); - if (CombinedFormatUtils.WORD_TAG.equals(params[0])) { - word = params[1]; - } else if (CombinedFormatUtils.PROBABILITY_TAG.equals(params[0])) { - probabilityInfo = new ProbabilityInfo(Integer.parseInt(params[1]), - probabilityInfo.mTimestamp, probabilityInfo.mLevel, - probabilityInfo.mCount); - } else if (CombinedFormatUtils.HISTORICAL_INFO_TAG.equals(params[0])) { - final String[] historicalInfoParams = - params[1].split(CombinedFormatUtils.HISTORICAL_INFO_SEPARATOR); - if (historicalInfoParams.length != HISTORICAL_INFO_ELEMENT_COUNT) { - throw new RuntimeException("Wrong format (historical info) : " + line); - } - probabilityInfo = new ProbabilityInfo(probabilityInfo.mProbability, - Integer.parseInt(historicalInfoParams[0]), - Integer.parseInt(historicalInfoParams[1]), - Integer.parseInt(historicalInfoParams[2])); - } else if (CombinedFormatUtils.NOT_A_WORD_TAG.equals(params[0])) { - isNotAWord = "true".equals(params[1]); + switch (params[0]) { + case CombinedFormatUtils.WORD_TAG: + word = params[1]; + break; + case CombinedFormatUtils.PROBABILITY_TAG: + probabilityInfo = new ProbabilityInfo(Integer.parseInt(params[1]), + probabilityInfo.mTimestamp, probabilityInfo.mLevel, + probabilityInfo.mCount); + break; + case CombinedFormatUtils.HISTORICAL_INFO_TAG: + final String[] historicalInfoParams = params[1].split( + CombinedFormatUtils.HISTORICAL_INFO_SEPARATOR); + if (historicalInfoParams.length != HISTORICAL_INFO_ELEMENT_COUNT) { + throw new RuntimeException("Wrong format (historical info) : " + + line); + } + probabilityInfo = new ProbabilityInfo(probabilityInfo.mProbability, + Integer.parseInt(historicalInfoParams[0]), + Integer.parseInt(historicalInfoParams[1]), + Integer.parseInt(historicalInfoParams[2])); + break; + case CombinedFormatUtils.NOT_A_WORD_TAG: + isNotAWord = CombinedFormatUtils.isLiteralTrue(params[1]); + break; + case CombinedFormatUtils.POSSIBLY_OFFENSIVE_TAG: + isPossiblyOffensive = CombinedFormatUtils.isLiteralTrue(params[1]); + break; } } } else if (args[0].matches(CombinedFormatUtils.SHORTCUT_TAG + "=.*")) { @@ -189,7 +200,8 @@ public class CombinedInputOutput { } } if (null != word) { - dict.add(word, probabilityInfo, shortcuts.isEmpty() ? null : shortcuts, isNotAWord); + dict.add(word, probabilityInfo, shortcuts.isEmpty() ? null : shortcuts, isNotAWord, + isPossiblyOffensive); for (WeightedString s : bigrams) { dict.setBigram(word, s.mWord, s.mProbabilityInfo); } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java index 0d93c7fa9..8fdf7633f 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java @@ -18,7 +18,9 @@ package com.android.inputmethod.latin.dicttool; public class CommandList { public static void populate() { + // TODO: Move some commands to native code. Dicttool.addCommand("info", Info.class); + Dicttool.addCommand("header", Header.class); Dicttool.addCommand("diff", Diff.class); Dicttool.addCommand("compress", Compress.Compressor.class); Dicttool.addCommand("uncompress", Compress.Uncompressor.class); diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java index 5dfb7bf11..6187853c8 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java @@ -27,9 +27,6 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.Ver2DictEncoder; import com.android.inputmethod.latin.makedict.Ver4DictEncoder; -import org.xml.sax.SAXException; - -import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -37,13 +34,10 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.LinkedList; -import javax.xml.parsers.ParserConfigurationException; - /** * Main class/method for DictionaryMaker. */ @@ -53,10 +47,7 @@ public class DictionaryMaker { private static final String OPTION_VERSION_2 = "-2"; private static final String OPTION_VERSION_4 = "-4"; private static final String OPTION_INPUT_SOURCE = "-s"; - private static final String OPTION_INPUT_BIGRAM_XML = "-b"; - private static final String OPTION_INPUT_SHORTCUT_XML = "-c"; private static final String OPTION_OUTPUT_BINARY = "-d"; - private static final String OPTION_OUTPUT_XML = "-x"; private static final String OPTION_OUTPUT_COMBINED = "-o"; private static final String OPTION_HELP = "-h"; private static final String OPTION_CODE_POINT_TABLE = "-t"; @@ -64,11 +55,7 @@ public class DictionaryMaker { private static final String OPTION_CODE_POINT_TABLE_ON = "on"; public final String mInputBinary; public final String mInputCombined; - public final String mInputUnigramXml; - public final String mInputShortcutXml; - public final String mInputBigramXml; public final String mOutputBinary; - public final String mOutputXml; public final String mOutputCombined; public final int mOutputBinaryFormatVersion; public final int mCodePointTableMode; @@ -77,39 +64,20 @@ public class DictionaryMaker { checkHasExactlyOneInput(); checkHasAtLeastOneOutput(); checkNotSameFile(mInputBinary, mOutputBinary); - checkNotSameFile(mInputBinary, mOutputXml); checkNotSameFile(mInputCombined, mOutputBinary); - checkNotSameFile(mInputCombined, mOutputXml); - checkNotSameFile(mInputUnigramXml, mOutputBinary); - checkNotSameFile(mInputUnigramXml, mOutputXml); - checkNotSameFile(mInputUnigramXml, mOutputCombined); - checkNotSameFile(mInputShortcutXml, mOutputBinary); - checkNotSameFile(mInputShortcutXml, mOutputXml); - checkNotSameFile(mInputShortcutXml, mOutputCombined); - checkNotSameFile(mInputBigramXml, mOutputBinary); - checkNotSameFile(mInputBigramXml, mOutputXml); - checkNotSameFile(mInputBigramXml, mOutputCombined); - checkNotSameFile(mOutputBinary, mOutputXml); checkNotSameFile(mOutputBinary, mOutputCombined); - checkNotSameFile(mOutputXml, mOutputCombined); } private void checkHasExactlyOneInput() { - if (null == mInputUnigramXml && null == mInputBinary && null == mInputCombined) { + if (null == mInputBinary && null == mInputCombined) { throw new RuntimeException("No input file specified"); - } else if ((null != mInputUnigramXml && null != mInputBinary) - || (null != mInputUnigramXml && null != mInputCombined) - || (null != mInputBinary && null != mInputCombined)) { + } else if (null != mInputBinary && null != mInputCombined) { throw new RuntimeException("Several input files specified"); - } else if ((null != mInputBinary || null != mInputCombined) - && (null != mInputBigramXml || null != mInputShortcutXml)) { - throw new RuntimeException("Separate bigrams/shortcut files are only supported" - + " with XML input (other formats include bigrams and shortcuts already)"); } } private void checkHasAtLeastOneOutput() { - if (null == mOutputBinary && null == mOutputXml && null == mOutputCombined) { + if (null == mOutputBinary && null == mOutputCombined) { throw new RuntimeException("No output specified"); } } @@ -126,22 +94,20 @@ public class DictionaryMaker { } } - private void displayHelp() { + private static void displayHelp() { MakedictLog.i(getHelp()); } public static String getHelp() { return "Usage: makedict " - + "[-s <unigrams.xml> [-b <bigrams.xml>] [-c <shortcuts_and_whitelist.xml>] " + "| [-s <combined format input]" - + "| [-s <binary input>] [-d <binary output>] [-x <xml output>] " + + "| [-s <binary input>] [-d <binary output>]" + " [-o <combined output>] [-t <code point table switch: on/off/auto>]" + "[-2] [-3] [-4]\n" + "\n" + " Converts a source dictionary file to one or several outputs.\n" - + " Source can be an XML file, with an optional XML bigrams file, or a\n" - + " binary dictionary file.\n" - + " Binary version 2 (Jelly Bean), 3, 4, XML and\n" + + " Source can be a binary dictionary file or a combined format file.\n" + + " Binary version 2 (Jelly Bean), 3, 4, and\n" + " combined format outputs are supported."; } @@ -152,13 +118,9 @@ public class DictionaryMaker { } String inputBinary = null; String inputCombined = null; - String inputUnigramXml = null; - String inputShortcutXml = null; - String inputBigramXml = null; String outputBinary = null; - String outputXml = null; String outputCombined = null; - int outputBinaryFormatVersion = FormatSpec.VERSION201; // the default version is 201. + int outputBinaryFormatVersion = FormatSpec.VERSION202; // the default version is 202. // Don't use code point table by default. int codePointTableMode = Ver2DictEncoder.CODE_POINT_TABLE_OFF; @@ -181,9 +143,7 @@ public class DictionaryMaker { String argValue = args.get(0); args.remove(0); if (OPTION_INPUT_SOURCE.equals(arg)) { - if (XmlDictInputOutput.isXmlUnigramDictionary(argValue)) { - inputUnigramXml = argValue; - } else if (CombinedInputOutput.isCombinedDictionary(argValue)) { + if (CombinedInputOutput.isCombinedDictionary(argValue)) { inputCombined = argValue; } else if (BinaryDictDecoderUtils.isBinaryDictionary(argValue)) { inputBinary = argValue; @@ -191,14 +151,8 @@ public class DictionaryMaker { throw new IllegalArgumentException( "Unknown format for file " + argValue); } - } else if (OPTION_INPUT_SHORTCUT_XML.equals(arg)) { - inputShortcutXml = argValue; - } else if (OPTION_INPUT_BIGRAM_XML.equals(arg)) { - inputBigramXml = argValue; } else if (OPTION_OUTPUT_BINARY.equals(arg)) { outputBinary = argValue; - } else if (OPTION_OUTPUT_XML.equals(arg)) { - outputXml = argValue; } else if (OPTION_OUTPUT_COMBINED.equals(arg)) { outputCombined = argValue; } else if (OPTION_CODE_POINT_TABLE.equals(arg)) { @@ -215,13 +169,13 @@ public class DictionaryMaker { } } } else { - if (null == inputBinary && null == inputUnigramXml) { + if (null == inputBinary) { if (BinaryDictDecoderUtils.isBinaryDictionary(arg)) { inputBinary = arg; } else if (CombinedInputOutput.isCombinedDictionary(arg)) { inputCombined = arg; } else { - inputUnigramXml = arg; + throw new IllegalArgumentException("Unknown format for file " + arg); } } else if (null == outputBinary) { outputBinary = arg; @@ -233,11 +187,7 @@ public class DictionaryMaker { mInputBinary = inputBinary; mInputCombined = inputCombined; - mInputUnigramXml = inputUnigramXml; - mInputShortcutXml = inputShortcutXml; - mInputBigramXml = inputBigramXml; mOutputBinary = outputBinary; - mOutputXml = outputXml; mOutputCombined = outputCombined; mOutputBinaryFormatVersion = outputBinaryFormatVersion; mCodePointTableMode = codePointTableMode; @@ -246,8 +196,7 @@ public class DictionaryMaker { } public static void main(String[] args) - throws FileNotFoundException, ParserConfigurationException, SAXException, IOException, - UnsupportedFormatException { + throws FileNotFoundException, IOException, UnsupportedFormatException { final Arguments parsedArgs = new Arguments(args); FusionDictionary dictionary = readInputFromParsedArgs(parsedArgs); writeOutputToParsedArgs(parsedArgs, dictionary); @@ -260,14 +209,11 @@ public class DictionaryMaker { * @return the read dictionary. */ private static FusionDictionary readInputFromParsedArgs(final Arguments args) - throws IOException, UnsupportedFormatException, ParserConfigurationException, - SAXException, FileNotFoundException { + throws IOException, UnsupportedFormatException, FileNotFoundException { if (null != args.mInputBinary) { return readBinaryFile(args.mInputBinary); } else if (null != args.mInputCombined) { return readCombinedFile(args.mInputCombined); - } else if (null != args.mInputUnigramXml) { - return readXmlFile(args.mInputUnigramXml, args.mInputShortcutXml, args.mInputBigramXml); } else { throw new RuntimeException("No input file specified"); } @@ -306,38 +252,6 @@ public class DictionaryMaker { } } - private static BufferedInputStream getBufferedFileInputStream(final String filename) - throws FileNotFoundException { - if (filename == null) { - return null; - } - return new BufferedInputStream(new FileInputStream(filename)); - } - - /** - * Read a dictionary from a unigram XML file, and optionally a bigram XML file. - * - * @param unigramXmlFilename the name of the unigram XML file. May not be null. - * @param shortcutXmlFilename the name of the shortcut/whitelist XML file, or null if none. - * @param bigramXmlFilename the name of the bigram XML file. Pass null if there are no bigrams. - * @return the read dictionary. - * @throws FileNotFoundException if one of the files can't be found - * @throws SAXException if one or more of the XML files is not well-formed - * @throws IOException if one the input files can't be read - * @throws ParserConfigurationException if the system can't create a SAX parser - */ - private static FusionDictionary readXmlFile(final String unigramXmlFilename, - final String shortcutXmlFilename, final String bigramXmlFilename) - throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { - try ( - final BufferedInputStream unigrams = getBufferedFileInputStream(unigramXmlFilename); - final BufferedInputStream shortcuts = getBufferedFileInputStream(shortcutXmlFilename); - final BufferedInputStream bigrams = getBufferedFileInputStream(bigramXmlFilename); - ) { - return XmlDictInputOutput.readDictionaryXml(unigrams, shortcuts, bigrams); - } - } - /** * Invoke the right output method according to args. * @@ -354,9 +268,6 @@ public class DictionaryMaker { writeBinaryDictionary(args.mOutputBinary, dict, args.mOutputBinaryFormatVersion, args.mCodePointTableMode); } - if (null != args.mOutputXml) { - writeXmlDictionary(args.mOutputXml, dict); - } if (null != args.mOutputCombined) { writeCombinedDictionary(args.mOutputCombined, dict); } @@ -388,21 +299,6 @@ public class DictionaryMaker { } /** - * Write the dictionary in XML format to the specified filename. - * - * @param outputFilename the name of the file to write to. - * @param dict the dictionary to write. - * @throws FileNotFoundException if the output file can't be created. - * @throws IOException if the output file can't be written to. - */ - private static void writeXmlDictionary(final String outputFilename, - final FusionDictionary dict) throws FileNotFoundException, IOException { - try (final BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilename))) { - XmlDictInputOutput.writeDictionaryXml(writer, dict); - } - } - - /** * Write the dictionary in the combined format to the specified filename. * * @param outputFilename the name of the file to write to. diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java index c6818ce0c..f97fbef2c 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java @@ -128,10 +128,10 @@ public class Diff extends Dicttool.Command { + word0Property.mIsNotAWord + " -> " + word1PtNode.getIsNotAWord()); hasDifferences = true; } - if (word0Property.mIsBlacklistEntry != word1PtNode.getIsBlacklistEntry()) { - System.out.println("Blacklist: " + word0Property.mWord + " " - + word0Property.mIsBlacklistEntry + " -> " - + word1PtNode.getIsBlacklistEntry()); + if (word0Property.mIsPossiblyOffensive != word1PtNode.getIsPossiblyOffensive()) { + System.out.println("Possibly-offensive: " + word0Property.mWord + " " + + word0Property.mIsPossiblyOffensive + " -> " + + word1PtNode.getIsPossiblyOffensive()); hasDifferences = true; } hasDifferences |= hasAttributesDifferencesAndPrintThemIfAny(word0Property.mWord, diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java new file mode 100644 index 000000000..51efdec33 --- /dev/null +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java @@ -0,0 +1,69 @@ +/** + * 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.dicttool; + +import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils.DecoderChainSpec; +import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; + +import java.io.File; +import java.util.Arrays; +import java.util.Locale; + +public class Header extends Dicttool.Command { + public static final String COMMAND = "header"; + + public Header() { + } + + @Override + public String getHelp() { + return COMMAND + " <filename>: prints the header contents of a dictionary file"; + } + + @Override + public void run() throws UnsupportedFormatException { + final boolean plumbing; + if (mArgs.length > 0 && "-p".equals(mArgs[0])) { + plumbing = true; + mArgs = Arrays.copyOfRange(mArgs, 1, mArgs.length); + } else { + plumbing = false; + } + if (mArgs.length < 1) { + throw new RuntimeException("Not enough arguments for command " + COMMAND); + } + final String filename = mArgs[0]; + final File dictFile = new File(filename); + final DecoderChainSpec<DictionaryHeader> spec = + BinaryDictOffdeviceUtils.decodeDictionaryForProcess(dictFile, + new BinaryDictOffdeviceUtils.HeaderReaderProcessor()); + if (null == spec) { + throw new UnsupportedFormatException(filename + + " doesn't seem to be a valid version 2 dictionary file"); + } + + final DictionaryHeader header = spec.mResult; + System.out.println("Dictionary : " + dictFile.getAbsolutePath()); + System.out.println("Size : " + dictFile.length() + " bytes"); + System.out.println("Format : Binary dictionary format"); + System.out.println("Packaging : " + spec.describeChain()); + System.out.println("Header attributes :"); + System.out.print(header.mDictionaryOptions.toString(2 /* indentCount */, plumbing)); + } +} diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java index 2850e1ff6..b8a64e31a 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java @@ -64,8 +64,7 @@ public class Info extends Dicttool.Command { + " whitelist entries)"); } - private static void showWordInfo(final FusionDictionary dict, final String word, - final boolean plumbing) { + private static void showWordInfo(final FusionDictionary dict, final String word) { final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word); if (null == ptNode) { System.out.println(word + " is not in the dictionary"); @@ -76,8 +75,8 @@ public class Info extends Dicttool.Command { if (ptNode.getIsNotAWord()) { System.out.println(" Is not a word"); } - if (ptNode.getIsBlacklistEntry()) { - System.out.println(" Is a blacklist entry"); + if (ptNode.getIsPossiblyOffensive()) { + System.out.println(" Is possibly offensive"); } final ArrayList<WeightedString> shortcutTargets = ptNode.getShortcutTargets(); if (null == shortcutTargets || shortcutTargets.isEmpty()) { @@ -124,7 +123,7 @@ public class Info extends Dicttool.Command { showInfo(dict, plumbing); } else { for (int i = 1; i < mArgs.length; ++i) { - showWordInfo(dict, mArgs[i], plumbing); + showWordInfo(dict, mArgs[i]); } } } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Makedict.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Makedict.java index 808e1d4c8..0b1fb88bc 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Makedict.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Makedict.java @@ -20,8 +20,6 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import java.io.FileNotFoundException; import java.io.IOException; -import javax.xml.parsers.ParserConfigurationException; -import org.xml.sax.SAXException; public class Makedict extends Dicttool.Command { public static final String COMMAND = "makedict"; @@ -35,8 +33,7 @@ public class Makedict extends Dicttool.Command { } @Override - public void run() throws FileNotFoundException, IOException, ParserConfigurationException, - SAXException, UnsupportedFormatException { + public void run() throws FileNotFoundException, IOException, UnsupportedFormatException { DictionaryMaker.main(mArgs); } } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java index 1f6798269..4e5c0742e 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java @@ -16,10 +16,8 @@ package com.android.inputmethod.latin.dicttool; -import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -79,16 +77,16 @@ public class Package { if (mArgs.length != 2) { throw new RuntimeException("Too many/too few arguments for command " + COMMAND); } - final BinaryDictOffdeviceUtils.DecoderChainSpec decodedSpec = + final BinaryDictOffdeviceUtils.DecoderChainSpec<File> decodedSpec = BinaryDictOffdeviceUtils.getRawDictionaryOrNull(new File(mArgs[0])); if (null == decodedSpec) { System.out.println(mArgs[0] + " does not seem to be a dictionary"); return; } System.out.println("Packaging : " + decodedSpec.describeChain()); - System.out.println("Uncompressed size : " + decodedSpec.mFile.length()); + System.out.println("Uncompressed size : " + decodedSpec.mResult.length()); try ( - final InputStream input = getFileInputStream(decodedSpec.mFile); + final InputStream input = getFileInputStream(decodedSpec.mResult); final OutputStream output = new BufferedOutputStream( getFileOutputStreamOrStdOut(mArgs[1])) ) { diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java deleted file mode 100644 index cd3ce70eb..000000000 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * 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.dicttool; - -import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; -import com.android.inputmethod.latin.makedict.ProbabilityInfo; -import com.android.inputmethod.latin.makedict.WeightedString; -import com.android.inputmethod.latin.makedict.WordProperty; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.TreeSet; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Reads and writes XML files for a FusionDictionary. - * - * All functions in this class are static. - */ -public class XmlDictInputOutput { - - private static final String ROOT_TAG = "wordlist"; - private static final String WORD_TAG = "w"; - private static final String BIGRAM_TAG = "bigram"; - private static final String SHORTCUT_TAG = "shortcut"; - private static final String PROBABILITY_ATTR = "f"; - private static final String WORD_ATTR = "word"; - private static final String NOT_A_WORD_ATTR = "not_a_word"; - - /** - * SAX handler for a unigram XML file. - */ - static private class UnigramHandler extends DefaultHandler { - // Parser states - private static final int START = 1; - private static final int WORD = 2; - private static final int UNKNOWN = 3; - private static final int SHORTCUT_ONLY_WORD_PROBABILITY = 1; - - FusionDictionary mDictionary; - int mState; // the state of the parser - int mFreq; // the currently read freq - String mWord; // the current word - final HashMap<String, ArrayList<WeightedString>> mShortcutsMap; - - /** - * Create the handler. - * - * @param shortcuts the shortcuts as a map. This may be empty, but may not be null. - */ - public UnigramHandler(final HashMap<String, ArrayList<WeightedString>> shortcuts) { - mDictionary = null; - mShortcutsMap = shortcuts; - mWord = ""; - mState = START; - mFreq = 0; - } - - public FusionDictionary getFinalDictionary() { - final FusionDictionary dict = mDictionary; - for (final String shortcutOnly : mShortcutsMap.keySet()) { - if (dict.hasWord(shortcutOnly)) continue; - dict.add(shortcutOnly, new ProbabilityInfo(SHORTCUT_ONLY_WORD_PROBABILITY), - mShortcutsMap.get(shortcutOnly), true /* isNotAWord */); - } - mDictionary = null; - mShortcutsMap.clear(); - mWord = ""; - mState = START; - mFreq = 0; - return dict; - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attrs) { - if (WORD_TAG.equals(localName)) { - mState = WORD; - mWord = ""; - for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) { - final String attrName = attrs.getLocalName(attrIndex); - if (PROBABILITY_ATTR.equals(attrName)) { - mFreq = Integer.parseInt(attrs.getValue(attrIndex)); - } - } - } else if (ROOT_TAG.equals(localName)) { - final HashMap<String, String> attributes = new HashMap<>(); - for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) { - final String attrName = attrs.getLocalName(attrIndex); - attributes.put(attrName, attrs.getValue(attrIndex)); - } - mDictionary = new FusionDictionary(new PtNodeArray(), - new DictionaryOptions(attributes)); - } else { - mState = UNKNOWN; - } - } - - @Override - public void characters(char[] ch, int start, int length) { - if (WORD == mState) { - // The XML parser is free to return text in arbitrary chunks one after the - // other. In particular, this happens in some implementations when it finds - // an escape code like "&". - mWord += String.copyValueOf(ch, start, length); - } - } - - @Override - public void endElement(String uri, String localName, String qName) { - if (WORD == mState) { - mDictionary.add(mWord, new ProbabilityInfo(mFreq), mShortcutsMap.get(mWord), - false /* isNotAWord */); - mState = START; - } - } - } - - static private class AssociativeListHandler extends DefaultHandler { - private final String SRC_TAG; - private final String SRC_ATTRIBUTE; - private final String DST_TAG; - private final String DST_ATTRIBUTE; - private final String DST_FREQ; - - // In this version of the XML file, the bigram frequency is given as an int 0..XML_MAX - private final static int XML_MAX = 256; - // In memory and in the binary dictionary the bigram frequency is 0..MEMORY_MAX - private final static int MEMORY_MAX = 256; - private final static int XML_TO_MEMORY_RATIO = XML_MAX / MEMORY_MAX; - - private String mSrc; - private final HashMap<String, ArrayList<WeightedString>> mAssocMap; - - public AssociativeListHandler(final String srcTag, final String srcAttribute, - final String dstTag, final String dstAttribute, final String dstFreq) { - SRC_TAG = srcTag; - SRC_ATTRIBUTE = srcAttribute; - DST_TAG = dstTag; - DST_ATTRIBUTE = dstAttribute; - DST_FREQ = dstFreq; - mSrc = null; - mAssocMap = new HashMap<>(); - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attrs) { - if (SRC_TAG.equals(localName)) { - mSrc = attrs.getValue(uri, SRC_ATTRIBUTE); - } else if (DST_TAG.equals(localName)) { - String dst = attrs.getValue(uri, DST_ATTRIBUTE); - int freq = getValueFromFreqString(attrs.getValue(uri, DST_FREQ)); - WeightedString bigram = new WeightedString(dst, freq / XML_TO_MEMORY_RATIO); - ArrayList<WeightedString> bigramList = mAssocMap.get(mSrc); - if (null == bigramList) bigramList = new ArrayList<>(); - bigramList.add(bigram); - mAssocMap.put(mSrc, bigramList); - } - } - - protected int getValueFromFreqString(final String freqString) { - return Integer.parseInt(freqString); - } - - // This may return an empty map, but will never return null. - public HashMap<String, ArrayList<WeightedString>> getAssocMap() { - return mAssocMap; - } - } - - /** - * SAX handler for a bigram XML file. - */ - static private class BigramHandler extends AssociativeListHandler { - private final static String BIGRAM_W1_TAG = "bi"; - private final static String BIGRAM_W2_TAG = "w"; - private final static String BIGRAM_W1_ATTRIBUTE = "w1"; - private final static String BIGRAM_W2_ATTRIBUTE = "w2"; - private final static String BIGRAM_FREQ_ATTRIBUTE = "p"; - - public BigramHandler() { - super(BIGRAM_W1_TAG, BIGRAM_W1_ATTRIBUTE, BIGRAM_W2_TAG, BIGRAM_W2_ATTRIBUTE, - BIGRAM_FREQ_ATTRIBUTE); - } - - // As per getAssocMap(), this never returns null. - public HashMap<String, ArrayList<WeightedString>> getBigramMap() { - return getAssocMap(); - } - } - - /** - * SAX handler for a shortcut & whitelist XML file. - */ - static private class ShortcutAndWhitelistHandler extends AssociativeListHandler { - private final static String ENTRY_TAG = "entry"; - private final static String ENTRY_ATTRIBUTE = "shortcut"; - private final static String TARGET_TAG = "target"; - private final static String REPLACEMENT_ATTRIBUTE = "replacement"; - private final static String TARGET_PRIORITY_ATTRIBUTE = "priority"; - private final static String WHITELIST_MARKER = "whitelist"; - private final static int WHITELIST_FREQ_VALUE = 15; - private final static int MIN_FREQ = 0; - private final static int MAX_FREQ = 14; - - public ShortcutAndWhitelistHandler() { - super(ENTRY_TAG, ENTRY_ATTRIBUTE, TARGET_TAG, REPLACEMENT_ATTRIBUTE, - TARGET_PRIORITY_ATTRIBUTE); - } - - @Override - protected int getValueFromFreqString(final String freqString) { - if (WHITELIST_MARKER.equals(freqString)) { - return WHITELIST_FREQ_VALUE; - } - final int intValue = super.getValueFromFreqString(freqString); - if (intValue < MIN_FREQ || intValue > MAX_FREQ) { - throw new RuntimeException("Shortcut freq out of range. Accepted range is " - + MIN_FREQ + ".." + MAX_FREQ); - } - return intValue; - } - - // As per getAssocMap(), this never returns null. - public HashMap<String, ArrayList<WeightedString>> getShortcutAndWhitelistMap() { - return getAssocMap(); - } - } - - /** - * Basic test to find out whether the file is in the unigram XML format or not. - * - * Concretely this only tests the header line. - * - * @param filename The name of the file to test. - * @return true if the file is in the unigram XML format, false otherwise - */ - public static boolean isXmlUnigramDictionary(final String filename) { - try (final BufferedReader reader = new BufferedReader( - new InputStreamReader(new FileInputStream(filename), "UTF-8"))) { - final String firstLine = reader.readLine(); - return firstLine.matches("^\\s*<wordlist .*>\\s*$"); - } catch (final IOException e) { - return false; - } - } - - /** - * Reads a dictionary from an XML file. - * - * This is the public method that will parse an XML file and return the corresponding memory - * representation. - * - * @param unigrams the file to read the data from. - * @param shortcuts the file to read the shortcuts & whitelist from, or null. - * @param bigrams the file to read the bigrams from, or null. - * @return the in-memory representation of the dictionary. - */ - public static FusionDictionary readDictionaryXml(final BufferedInputStream unigrams, - final BufferedInputStream shortcuts, final BufferedInputStream bigrams) - throws SAXException, IOException, ParserConfigurationException { - final SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - final SAXParser parser = factory.newSAXParser(); - final BigramHandler bigramHandler = new BigramHandler(); - if (null != bigrams) parser.parse(bigrams, bigramHandler); - - final ShortcutAndWhitelistHandler shortcutAndWhitelistHandler = - new ShortcutAndWhitelistHandler(); - if (null != shortcuts) parser.parse(shortcuts, shortcutAndWhitelistHandler); - - final UnigramHandler unigramHandler = - new UnigramHandler(shortcutAndWhitelistHandler.getShortcutAndWhitelistMap()); - parser.parse(unigrams, unigramHandler); - final FusionDictionary dict = unigramHandler.getFinalDictionary(); - final HashMap<String, ArrayList<WeightedString>> bigramMap = bigramHandler.getBigramMap(); - for (final String firstWord : bigramMap.keySet()) { - if (!dict.hasWord(firstWord)) continue; - final ArrayList<WeightedString> bigramList = bigramMap.get(firstWord); - for (final WeightedString bigram : bigramList) { - if (!dict.hasWord(bigram.mWord)) continue; - dict.setBigram(firstWord, bigram.mWord, bigram.mProbabilityInfo); - } - } - return dict; - } - - /** - * Reads a dictionary in the first, legacy XML format - * - * This method reads data from the parser and creates a new FusionDictionary with it. - * The format parsed by this method is the format used before Ice Cream Sandwich, - * which has no support for bigrams or shortcuts/whitelist. - * It is important to note that this method expects the parser to have already eaten - * the first, all-encompassing tag. - * - * @param xpp the parser to read the data from. - * @return the parsed dictionary. - */ - - /** - * Writes a dictionary to an XML file. - * - * The output format is the "second" format, which supports bigrams and shortcuts/whitelist. - * - * @param destination a destination stream to write to. - * @param dict the dictionary to write. - */ - public static void writeDictionaryXml(final BufferedWriter destination, - final FusionDictionary dict) throws IOException { - final TreeSet<WordProperty> wordPropertiesInDict = new TreeSet<>(); - for (WordProperty wordProperty : dict) { - wordPropertiesInDict.add(wordProperty); - } - // TODO: use an XMLSerializer if this gets big - destination.write("<wordlist format=\"2\""); - for (final String key : dict.mOptions.mAttributes.keySet()) { - final String value = dict.mOptions.mAttributes.get(key); - destination.write(" " + key + "=\"" + value + "\""); - } - destination.write(">\n"); - destination.write("<!-- Warning: there is no code to read this format yet. -->\n"); - for (WordProperty wordProperty : wordPropertiesInDict) { - destination.write(" <" + WORD_TAG + " " + WORD_ATTR + "=\"" + wordProperty.mWord - + "\" " + PROBABILITY_ATTR + "=\"" + wordProperty.getProbability() - + (wordProperty.mIsNotAWord ? "\" " + NOT_A_WORD_ATTR + "=\"true" : "") - + "\">"); - if (wordProperty.mHasShortcuts) { - destination.write("\n"); - for (WeightedString target : wordProperty.mShortcutTargets) { - destination.write(" <" + SHORTCUT_TAG + " " + PROBABILITY_ATTR + "=\"" - + target.getProbability() + "\">" + target.mWord + "</" + SHORTCUT_TAG - + ">\n"); - } - destination.write(" "); - } - if (wordProperty.mHasNgrams) { - destination.write("\n"); - for (WeightedString bigram : wordProperty.getBigrams()) { - destination.write(" <" + BIGRAM_TAG + " " + PROBABILITY_ATTR + "=\"" - + bigram.getProbability() + "\">" + bigram.mWord - + "</" + BIGRAM_TAG + ">\n"); - } - destination.write(" "); - } - destination.write("</" + WORD_TAG + ">\n"); - } - destination.write("</wordlist>\n"); - destination.close(); - } -} diff --git a/tools/dicttool/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/tools/dicttool/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index a4ad6b514..dcc4e1972 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin.personalization; +@SuppressWarnings("unused") public class PersonalizationHelper { public static void currentTimeChangedForTesting(final int currentTimestamp) { } diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java index 0236a446d..ea9d4cc19 100644 --- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java +++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java @@ -16,10 +16,17 @@ package com.android.inputmethod.latin.dicttool; +import com.android.inputmethod.latin.common.CodePointUtils; +import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils; +import com.android.inputmethod.latin.dicttool.Compress; +import com.android.inputmethod.latin.dicttool.Crypt; +import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils.DecoderChainSpec; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; +import com.android.inputmethod.latin.makedict.BinaryDictUtils; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.DictEncoder; import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary; @@ -35,13 +42,37 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; /** * Unit tests for BinaryDictOffdeviceUtils */ public class BinaryDictOffdeviceUtilsTests extends TestCase { private static final int TEST_FREQ = 37; // Some arbitrary value unlikely to happen by chance + private static final int CODE_POINT_SET_SIZE = 300; + final Random mRandom; + private static final ArrayList<String> sWords = new ArrayList<>(); + + public BinaryDictOffdeviceUtilsTests(final long seed, final int maxUnigrams) { + super(); + mRandom = new Random(seed); + sWords.clear(); + generateWords(maxUnigrams, mRandom); + } + + private static void generateWords(final int maxUnigrams, final Random random) { + final int[] codePointSet = CodePointUtils.generateCodePointSet( + CODE_POINT_SET_SIZE, random); + final Set<String> wordSet = new HashSet<>(); + while (wordSet.size() < maxUnigrams) { + wordSet.add(CodePointUtils.generateWord(random, codePointSet)); + } + sWords.addAll(wordSet); + } public void testGetRawDictWorks() throws IOException, UnsupportedFormatException { final String VERSION = "1"; @@ -54,31 +85,31 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase { testOptions.mAttributes.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, LOCALE); testOptions.mAttributes.put(DictionaryHeader.DICTIONARY_ID_KEY, ID); final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), testOptions); - dict.add("foo", new ProbabilityInfo(TEST_FREQ), null, false /* isNotAWord */); - dict.add("fta", new ProbabilityInfo(1), null, false /* isNotAWord */); - dict.add("ftb", new ProbabilityInfo(1), null, false /* isNotAWord */); - dict.add("bar", new ProbabilityInfo(1), null, false /* isNotAWord */); - dict.add("fool", new ProbabilityInfo(1), null, false /* isNotAWord */); + dict.add("foo", new ProbabilityInfo(TEST_FREQ), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("fta", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("ftb", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("bar", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("fool", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); final File dst = File.createTempFile("testGetRawDict", ".tmp"); dst.deleteOnExit(); try (final OutputStream out = Compress.getCompressedStream( - Compress.getCompressedStream( - Compress.getCompressedStream( - new BufferedOutputStream(new FileOutputStream(dst)))))) { + new BufferedOutputStream(new FileOutputStream(dst)))) { final DictEncoder dictEncoder = new Ver2DictEncoder(out); - dictEncoder.writeDictionary(dict, new FormatOptions(2, false)); + dictEncoder.writeDictionary(dict, new FormatOptions(FormatSpec.VERSION202, false)); } // Test for an actually compressed dictionary and its contents - final BinaryDictOffdeviceUtils.DecoderChainSpec decodeSpec = + final BinaryDictOffdeviceUtils.DecoderChainSpec<File> decodeSpec = BinaryDictOffdeviceUtils.getRawDictionaryOrNull(dst); - for (final String step : decodeSpec.mDecoderSpec) { - assertEquals("Wrong decode spec", BinaryDictOffdeviceUtils.COMPRESSION, step); - } - assertEquals("Wrong decode spec", 3, decodeSpec.mDecoderSpec.size()); - final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(decodeSpec.mFile, 0, - decodeSpec.mFile.length()); + assertEquals("Wrong decode spec", "raw > compression", decodeSpec.describeChain()); + final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(decodeSpec.mResult, 0, + decodeSpec.mResult.length()); final FusionDictionary resultDict = dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */); assertEquals("Wrong version attribute", VERSION, resultDict.mOptions.mAttributes.get( @@ -119,4 +150,64 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase { assertNull("Wrongly identified data file", BinaryDictOffdeviceUtils.getRawDictionaryOrNull(gzDst)); } + + public void runTestHeaderReaderProcessorWithOneSpec(final boolean compress, final boolean crypt) + throws IOException, UnsupportedFormatException { + final String dictName = "testHeaderReaderProcessor"; + final String dictVersion = Long.toString(System.currentTimeMillis()); + final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS; + final int MAX_NUMBER_OF_OPTIONS_TO_ADD = 5; + final HashMap<String, String> options = new HashMap<>(); + // Required attributes + options.put("dictionary", "main:en_US"); + options.put("locale", "en_US"); + options.put("version", Integer.toString(mRandom.nextInt())); + // Add some random options for test + final int numberOfOptionsToAdd = mRandom.nextInt() % (MAX_NUMBER_OF_OPTIONS_TO_ADD + 1); + for (int i = 0; i < numberOfOptionsToAdd; ++i) { + options.put(sWords.get(2 * i), sWords.get(2 * 1 + 1)); + } + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + new DictionaryOptions(options)); + + for (int i = 0; i < sWords.size(); ++i) { + final String word = sWords.get(i); + dict.add(word, new ProbabilityInfo(TEST_FREQ), null /* shortcuts */, + false /* isNotAWord */, false /* isPossiblyOffensive */); + } + + File file = File.createTempFile(dictName, ".tmp"); + final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions); + dictEncoder.writeDictionary(dict, formatOptions); + + if (compress) { + final File rawFile = file; + file = File.createTempFile(dictName + ".compress", ".tmp"); + final Compress.Compressor compressCommand = new Compress.Compressor(); + compressCommand.setArgs(new String[] { rawFile.getPath(), file.getPath() }); + compressCommand.run(); + } + if (crypt) { + final File rawFile = file; + file = File.createTempFile(dictName + ".crypt", ".tmp"); + final Crypt.Encrypter cryptCommand = new Crypt.Encrypter(); + cryptCommand.setArgs(new String[] { rawFile.getPath(), file.getPath() }); + cryptCommand.run(); + } + + final DecoderChainSpec<DictionaryHeader> spec = + BinaryDictOffdeviceUtils.decodeDictionaryForProcess(file, + new BinaryDictOffdeviceUtils.HeaderReaderProcessor()); + assertNotNull("Can't decode a dictionary we just wrote : " + file, spec); + final DictionaryHeader header = spec.mResult; + assertEquals("raw" + (crypt ? " > encryption" : "") + (compress ? " > compression" : ""), + spec.describeChain()); + assertEquals(header.mDictionaryOptions.mAttributes, options); + } + + public void testHeaderReaderProcessor() throws IOException, UnsupportedFormatException { + runTestHeaderReaderProcessorWithOneSpec(false /* compress */, false /* crypt */); + runTestHeaderReaderProcessorWithOneSpec(true /* compress */, false /* crypt */); + runTestHeaderReaderProcessorWithOneSpec(true /* compress */, true /* crypt */); + } } diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictEncoderFlattenTreeTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictEncoderFlattenTreeTests.java index aa228e72c..dc9981d1a 100644 --- a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictEncoderFlattenTreeTests.java +++ b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictEncoderFlattenTreeTests.java @@ -33,11 +33,16 @@ public class BinaryDictEncoderFlattenTreeTests extends TestCase { public void testFlattenNodes() { final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), new DictionaryOptions(new HashMap<String, String>())); - dict.add("foo", new ProbabilityInfo(1), null, false /* isNotAWord */); - dict.add("fta", new ProbabilityInfo(1), null, false /* isNotAWord */); - dict.add("ftb", new ProbabilityInfo(1), null, false /* isNotAWord */); - dict.add("bar", new ProbabilityInfo(1), null, false /* isNotAWord */); - dict.add("fool", new ProbabilityInfo(1), null, false /* isNotAWord */); + dict.add("foo", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("fta", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("ftb", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("bar", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); + dict.add("fool", new ProbabilityInfo(1), null, false /* isNotAWord */, + false /* isPossiblyOffensive */); final ArrayList<PtNodeArray> result = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray); assertEquals(4, result.size()); diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java index 71f8ac8d4..1a4f096e4 100644 --- a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java +++ b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java @@ -67,17 +67,18 @@ public class FusionDictionaryTest extends TestCase { } } - private void checkDictionary(final FusionDictionary dict, final ArrayList<String> words, - int limit) { + private static void checkDictionary(final FusionDictionary dict, final ArrayList<String> words, + final int limit) { assertNotNull(dict); + int count = limit; for (final String word : words) { - if (--limit < 0) return; + if (--count < 0) return; final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word); assertNotNull(ptNode); } } - private String dumpWord(final String word) { + private static String dumpWord(final String word) { final StringBuilder sb = new StringBuilder(""); for (int i = 0; i < word.length(); i = word.offsetByCodePoints(i, 1)) { sb.append(word.codePointAt(i)); @@ -86,7 +87,7 @@ public class FusionDictionaryTest extends TestCase { return sb.toString(); } - private void dumpDict(final FusionDictionary dict) { + private static void dumpDict(final FusionDictionary dict) { for (WordProperty wordProperty : dict) { System.out.println("Word " + dumpWord(wordProperty.mWord)); } @@ -101,7 +102,8 @@ public class FusionDictionaryTest extends TestCase { prepare(time); for (int i = 0; i < sWords.size(); ++i) { System.out.println("Adding in pos " + i + " : " + dumpWord(sWords.get(i))); - dict.add(sWords.get(i), new ProbabilityInfo(180), null, false); + dict.add(sWords.get(i), new ProbabilityInfo(180), null, false, + false /* isPossiblyOffensive */); dumpDict(dict); checkDictionary(dict, sWords, i); } |