aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--common/Android.mk28
-rw-r--r--common/src/com/android/inputmethod/annotations/ExternallyReferenced.java (renamed from java/src/com/android/inputmethod/annotations/ExternallyReferenced.java)0
-rw-r--r--common/src/com/android/inputmethod/annotations/UsedForTesting.java (renamed from java/src/com/android/inputmethod/annotations/UsedForTesting.java)0
-rw-r--r--common/src/com/android/inputmethod/latin/common/CodePointUtils.java (renamed from tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java)28
-rw-r--r--common/src/com/android/inputmethod/latin/common/CollectionUtils.java66
-rw-r--r--common/src/com/android/inputmethod/latin/common/ComposedData.java66
-rw-r--r--common/src/com/android/inputmethod/latin/common/Constants.java (renamed from java/src/com/android/inputmethod/latin/Constants.java)64
-rw-r--r--common/src/com/android/inputmethod/latin/common/CoordinateUtils.java (renamed from java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java)37
-rw-r--r--common/src/com/android/inputmethod/latin/common/FileUtils.java (renamed from java/src/com/android/inputmethod/latin/utils/FileUtils.java)2
-rw-r--r--common/src/com/android/inputmethod/latin/common/InputPointers.java (renamed from java/src/com/android/inputmethod/latin/InputPointers.java)65
-rw-r--r--common/src/com/android/inputmethod/latin/common/LocaleUtils.java (renamed from java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java)84
-rw-r--r--common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java (renamed from java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java)27
-rw-r--r--common/src/com/android/inputmethod/latin/common/ResizableIntArray.java (renamed from java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java)15
-rw-r--r--common/src/com/android/inputmethod/latin/common/StringUtils.java (renamed from java/src/com/android/inputmethod/latin/utils/StringUtils.java)336
-rw-r--r--common/src/com/android/inputmethod/latin/common/UnicodeSurrogate.java38
15 files changed, 574 insertions, 282 deletions
diff --git a/common/Android.mk b/common/Android.mk
new file mode 100644
index 000000000..132a22358
--- /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/CollectionUtils.java b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java
new file mode 100644
index 000000000..48df413fd
--- /dev/null
+++ b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.common;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Utility methods for working with collections.
+ */
+public final class CollectionUtils {
+ private CollectionUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ /**
+ * Converts a sub-range of the given array to an ArrayList of the appropriate type.
+ * @param array Array to be converted.
+ * @param start First index inclusive to be converted.
+ * @param end Last index exclusive to be converted.
+ * @throws IllegalArgumentException if start or end are out of range or start &gt; end.
+ */
+ @Nonnull
+ public static <E> ArrayList<E> arrayAsList(@Nonnull final E[] array, final int start,
+ final int end) {
+ if (start < 0 || start > end || end > array.length) {
+ throw new IllegalArgumentException("Invalid start: " + start + " end: " + end
+ + " with array.length: " + array.length);
+ }
+
+ final ArrayList<E> list = new ArrayList<>(end - start);
+ for (int i = start; i < end; i++) {
+ list.add(array[i]);
+ }
+ return list;
+ }
+
+ /**
+ * Tests whether c contains no elements, true if c is null or c is empty.
+ * @param c Collection to test.
+ * @return Whether c contains no elements.
+ */
+ @UsedForTesting
+ public static boolean isNullOrEmpty(@Nullable final Collection<?> c) {
+ return c == null || c.isEmpty();
+ }
+}
diff --git a/common/src/com/android/inputmethod/latin/common/ComposedData.java b/common/src/com/android/inputmethod/latin/common/ComposedData.java
new file mode 100644
index 000000000..7f0966050
--- /dev/null
+++ b/common/src/com/android/inputmethod/latin/common/ComposedData.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.common;
+
+import javax.annotation.Nonnull;
+
+/**
+ * An immutable class that encapsulates a snapshot of word composition data.
+ */
+public class ComposedData {
+ @Nonnull
+ public final InputPointers mInputPointers;
+ public final boolean mIsBatchMode;
+ @Nonnull
+ public final String mTypedWord;
+
+ public ComposedData(@Nonnull final InputPointers inputPointers, final boolean isBatchMode,
+ @Nonnull final String typedWord) {
+ mInputPointers = inputPointers;
+ mIsBatchMode = isBatchMode;
+ mTypedWord = typedWord;
+ }
+
+ /**
+ * Copy the code points in the typed word to a destination array of ints.
+ *
+ * If the array is too small to hold the code points in the typed word, nothing is copied and
+ * -1 is returned.
+ *
+ * @param destination the array of ints.
+ * @return the number of copied code points.
+ */
+ public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
+ @Nonnull final int[] destination) {
+ // lastIndex is exclusive
+ final int lastIndex = mTypedWord.length()
+ - StringUtils.getTrailingSingleQuotesCount(mTypedWord);
+ if (lastIndex <= 0) {
+ // The string is empty or contains only single quotes.
+ return 0;
+ }
+
+ // The following function counts the number of code points in the text range which begins
+ // at index 0 and extends to the character at lastIndex.
+ final int codePointSize = Character.codePointCount(mTypedWord, 0, lastIndex);
+ if (codePointSize > destination.length) {
+ return -1;
+ }
+ return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWord, 0,
+ lastIndex, true /* downCase */);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/common/src/com/android/inputmethod/latin/common/Constants.java
index 43af66eb7..b491c8cfd 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/common/src/com/android/inputmethod/latin/common/Constants.java
@@ -14,9 +14,14 @@
* 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 {
/**
* The alpha value for fully opaque.
@@ -57,6 +62,13 @@ public final class Constants {
@SuppressWarnings("dep-ann")
public static final String FORCE_ASCII = "forceAscii";
+ /**
+ * The private IME option used to suppress the floating gesture preview for a given text
+ * field. This overrides the corresponding keyboard settings preference.
+ * {@link com.android.inputmethod.latin.settings.SettingsValues#mGestureFloatingPreviewTextEnabled}
+ */
+ public static final String NO_FLOATING_GESTURE_PREVIEW = "noGestureFloatingPreview";
+
private ImeOption() {
// This utility class is not publicly instantiable.
}
@@ -152,7 +164,6 @@ public final class Constants {
// TODO: replace the following constants with state in InputTransaction?
public static final int NOT_A_COORDINATE = -1;
public static final int SUGGESTION_STRIP_COORDINATE = -2;
- public static final int SPELL_CHECKER_COORDINATE = -3;
public static final int EXTERNAL_KEYBOARD_COORDINATE = -4;
// A hint on how many characters to cache from the TextView. A good value of this is given by
@@ -163,13 +174,6 @@ public final class Constants {
// right for this.
public static final int MAX_CHARACTERS_FOR_RECAPITALIZATION = 1024 * 100;
- // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
- public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
-
- // (MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1)-gram is supported in Java side. Needs to modify
- // MAX_PREV_WORD_COUNT_FOR_N_GRAM in native/jni/src/defines.h for suggestions.
- public static final int MAX_PREV_WORD_COUNT_FOR_N_GRAM = 2;
-
// Key events coming any faster than this are long-presses.
public static final int LONG_PRESS_MILLISECONDS = 200;
// TODO: Set this value appropriately.
@@ -203,8 +207,6 @@ public final class Constants {
public static final int CODE_DASH = '-';
public static final int CODE_SINGLE_QUOTE = '\'';
public static final int CODE_DOUBLE_QUOTE = '"';
- public static final int CODE_QUESTION_MARK = '?';
- public static final int CODE_EXCLAMATION_MARK = '!';
public static final int CODE_SLASH = '/';
public static final int CODE_BACKSLASH = '\\';
public static final int CODE_VERTICAL_BAR = '|';
@@ -217,15 +219,17 @@ public final class Constants {
public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
public static final int CODE_INVERTED_QUESTION_MARK = 0xBF; // ¿
public static final int CODE_INVERTED_EXCLAMATION_MARK = 0xA1; // ¡
+ public static final int CODE_GRAVE_ACCENT = '`';
+ public static final int CODE_CIRCUMFLEX_ACCENT = '^';
+ public static final int CODE_TILDE = '~';
public static final String REGEXP_PERIOD = "\\.";
public static final String STRING_SPACE = " ";
- public static final String STRING_PERIOD_AND_SPACE = ". ";
/**
* 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;
@@ -248,6 +252,7 @@ public final class Constants {
return code >= CODE_SPACE;
}
+ @Nonnull
public static String printableCode(final int code) {
switch (code) {
case CODE_SHIFT: return "shift";
@@ -275,7 +280,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) {
@@ -287,24 +293,42 @@ 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;
+ public static final int MAX_IME_DECODER_RESULTS = 20;
+ public static final int DECODER_SCORE_SCALAR = 1000000;
+ public static final int DECODER_MAX_SCORE = 1000000000;
+
+ public static final int EVENT_BACKSPACE = 1;
+ public static final int EVENT_REJECTION = 2;
+ public static final int EVENT_REVERT = 3;
+
private Constants() {
// This utility class is not publicly instantiable.
}
diff --git a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java
index 87df013a6..031662411 100644
--- a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
-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/FileUtils.java b/common/src/com/android/inputmethod/latin/common/FileUtils.java
index f1106a6c6..676845842 100644
--- a/java/src/com/android/inputmethod/latin/utils/FileUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/FileUtils.java
@@ -14,7 +14,7 @@
* the License.
*/
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
import java.io.File;
import java.io.FilenameFilter;
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/common/src/com/android/inputmethod/latin/common/InputPointers.java
index 790e0d830..4b2ae7e76 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;
}
@@ -114,6 +112,7 @@ public final class InputPointers {
* Shift to the left by elementCount, discarding elementCount pointers at the start.
* @param elementCount how many elements to shift.
*/
+ @UsedForTesting
public void shift(final int elementCount) {
mXCoordinates.shift(elementCount);
mYCoordinates.shift(elementCount);
@@ -133,24 +132,29 @@ 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();
}
+ /**
+ * Gets the time each point was registered, in milliseconds, relative to the first event in the
+ * sequence.
+ * @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();
}
@@ -159,25 +163,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/dictionarypack/LocaleUtils.java b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
index 4f0805c5c..d5878c024 100644
--- a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
@@ -14,15 +14,15 @@
* the License.
*/
-package com.android.inputmethod.dictionarypack;
-
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.text.TextUtils;
+package com.android.inputmethod.latin.common;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Locale;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
/**
* A class to help with handling Locales in string form.
*
@@ -104,9 +104,10 @@ public final class LocaleUtils {
* @param testedLocale the locale to test.
* @return a constant that measures how well the tested locale matches the reference locale.
*/
- public static int getMatchLevel(final String referenceLocale, final String testedLocale) {
- if (TextUtils.isEmpty(referenceLocale)) {
- return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
+ public static int getMatchLevel(@Nullable final String referenceLocale,
+ @Nullable final String testedLocale) {
+ if (StringUtils.isEmpty(referenceLocale)) {
+ return StringUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
}
if (null == testedLocale) return LOCALE_NO_MATCH;
final String[] referenceParams = referenceLocale.split("_", 3);
@@ -160,45 +161,50 @@ public final class LocaleUtils {
return LOCALE_MATCH <= level;
}
- /**
- * Sets the system locale for this process.
- *
- * @param res the resources to use. Pass current resources.
- * @param newLocale the locale to change to.
- * @return the old locale.
- */
- public static Locale setSystemLocale(final Resources res, final Locale newLocale) {
- final Configuration conf = res.getConfiguration();
- final Locale saveLocale = conf.locale;
- conf.locale = newLocale;
- res.updateConfiguration(conf, res.getDisplayMetrics());
- return saveLocale;
- }
-
private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
/**
* Creates a locale from a string specification.
+ * @param localeString a string specification of a locale, in a format of "ll_cc_variant" where
+ * "ll" is a language code, "cc" is a country code.
*/
- public static Locale constructLocaleFromString(final String localeStr) {
- if (localeStr == null)
- return null;
+ @Nonnull
+ public static Locale constructLocaleFromString(@Nonnull final String localeString) {
synchronized (sLocaleCache) {
- if (sLocaleCache.containsKey(localeStr))
- return sLocaleCache.get(localeStr);
- Locale retval = null;
- String[] localeParams = localeStr.split("_", 3);
- if (localeParams.length == 1) {
- retval = new Locale(localeParams[0]);
- } else if (localeParams.length == 2) {
- retval = new Locale(localeParams[0], localeParams[1]);
- } else if (localeParams.length == 3) {
- retval = new Locale(localeParams[0], localeParams[1], localeParams[2]);
+ if (sLocaleCache.containsKey(localeString)) {
+ return sLocaleCache.get(localeString);
}
- if (retval != null) {
- sLocaleCache.put(localeStr, retval);
+ final String[] elements = localeString.split("_", 3);
+ final Locale locale;
+ if (elements.length == 1) {
+ locale = new Locale(elements[0] /* language */);
+ } else if (elements.length == 2) {
+ locale = new Locale(elements[0] /* language */, elements[1] /* country */);
+ } else { // localeParams.length == 3
+ locale = new Locale(elements[0] /* language */, elements[1] /* country */,
+ elements[2] /* variant */);
}
- return retval;
+ sLocaleCache.put(localeString, locale);
+ return locale;
}
}
+
+ // TODO: Get this information from the framework instead of maintaining here by ourselves.
+ private static final HashSet<String> sRtlLanguageCodes = new HashSet<>();
+ static {
+ // List of known Right-To-Left language codes.
+ sRtlLanguageCodes.add("ar"); // Arabic
+ sRtlLanguageCodes.add("fa"); // Persian
+ sRtlLanguageCodes.add("iw"); // Hebrew
+ sRtlLanguageCodes.add("ku"); // Kurdish
+ sRtlLanguageCodes.add("ps"); // Pashto
+ sRtlLanguageCodes.add("sd"); // Sindhi
+ sRtlLanguageCodes.add("ug"); // Uyghur
+ sRtlLanguageCodes.add("ur"); // Urdu
+ sRtlLanguageCodes.add("yi"); // Yiddish
+ }
+
+ public static boolean isRtlLanguage(@Nonnull final Locale locale) {
+ return sRtlLanguageCodes.contains(locale.getLanguage());
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java
index 31a20c4db..d673442ca 100644
--- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
+++ b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin.settings;
+package com.android.inputmethod.latin.common;
public class NativeSuggestOptions {
// Need to update suggest_options.h when you add, remove or reorder options.
@@ -22,10 +22,14 @@ public class NativeSuggestOptions {
private static final int USE_FULL_EDIT_DISTANCE = 1;
private static final int BLOCK_OFFENSIVE_WORDS = 2;
private static final int SPACE_AWARE_GESTURE_ENABLED = 3;
- private static final int OPTIONS_SIZE = 4;
+ private static final int WEIGHT_FOR_LOCALE_IN_THOUSANDS = 4;
+ private static final int OPTIONS_SIZE = 5;
- private final int[] mOptions = new int[OPTIONS_SIZE
- + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
+ private final int[] mOptions;
+
+ public NativeSuggestOptions() {
+ mOptions = new int[OPTIONS_SIZE];
+ }
public void setIsGesture(final boolean value) {
setBooleanOption(IS_GESTURE, value);
@@ -39,17 +43,10 @@ public class NativeSuggestOptions {
setBooleanOption(BLOCK_OFFENSIVE_WORDS, value);
}
- public void setSpaceAwareGestureEnabled(final boolean value) {
- setBooleanOption(SPACE_AWARE_GESTURE_ENABLED, value);
- }
-
- public void setAdditionalFeaturesOptions(final int[] additionalOptions) {
- if (additionalOptions == null) {
- return;
- }
- for (int i = 0; i < additionalOptions.length; i++) {
- setIntegerOption(OPTIONS_SIZE + i, additionalOptions[i]);
- }
+ public void setWeightForLocale(final float value) {
+ // We're passing this option as a fixed point value, in thousands. This is decoded in
+ // native code by SuggestOptions#weightForLocale().
+ setIntegerOption(WEIGHT_FOR_LOCALE_IN_THOUSANDS, (int) (value * 1000));
}
public int[] getOptions() {
diff --git a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java
index 64c9e2cff..77f5c4cd5 100644
--- a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java
+++ b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java
@@ -14,12 +14,17 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
+
+import com.android.inputmethod.annotations.UsedForTesting;
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 +94,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 +115,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;
}
@@ -136,6 +142,7 @@ public final class ResizableIntArray {
* Shift to the left by elementCount, discarding elementCount pointers at the start.
* @param elementCount how many elements to shift.
*/
+ @UsedForTesting
public void shift(final int elementCount) {
System.arraycopy(mArray, elementCount, mArray, 0, mLength - elementCount);
mLength -= elementCount;
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/common/src/com/android/inputmethod/latin/common/StringUtils.java
index 79128dbd2..572f0cd9b 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 String text) {
- if (TextUtils.isEmpty(text)) return 0;
- return text.codePointCount(0, text.length());
+ // 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();
}
- public static String newSingleCodePointString(int codePoint) {
+ // 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());
+ }
+
+ @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,20 +197,24 @@ 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 toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale);
+ return s.toUpperCase(getLocaleUsedForToTitleCase(locale));
}
// Please refer to the comment below in
// {@link #capitalizeFirstAndDowncaseRest(String,Locale)} as this has the same shortcomings
final int cutoff = s.offsetByCodePoints(0, 1);
- return toUpperCaseOfStringForLocale(
- s.substring(0, cutoff), true /* needsToUpperCase */, locale) + s.substring(cutoff);
+ return s.substring(0, cutoff).toUpperCase(getLocaleUsedForToTitleCase(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 toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale);
+ return s.toUpperCase(getLocaleUsedForToTitleCase(locale));
}
// TODO: fix the bugs below
// - It does not work for Serbian, because it fails to account for the "lj" character,
@@ -152,17 +224,18 @@ public final class StringUtils {
// be capitalized as "IJ" as if they were a single letter in most words (not all). If the
// unicode char for the ligature is used however, it works.
final int cutoff = s.offsetByCodePoints(0, 1);
- final String titleCaseFirstLetter = toUpperCaseOfStringForLocale(
- s.substring(0, cutoff), true /* needsToUpperCase */, locale);
- return titleCaseFirstLetter + s.substring(cutoff).toLowerCase(locale);
+ return s.substring(0, cutoff).toUpperCase(getLocaleUsedForToTitleCase(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.
@@ -170,7 +243,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) {
@@ -201,8 +275,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;
@@ -216,7 +290,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;
@@ -229,7 +304,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) {
@@ -241,7 +318,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();
@@ -277,7 +354,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) {
@@ -290,7 +367,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) {
@@ -303,8 +380,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)) {
@@ -323,8 +400,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();
@@ -358,9 +436,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;
@@ -397,11 +477,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;
}
@@ -422,18 +508,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
@@ -448,7 +540,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))) {
@@ -459,12 +551,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();
@@ -474,8 +567,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();
@@ -493,7 +587,8 @@ public final class StringUtils {
private static final String LANGUAGE_GREEK = "el";
- private static Locale getLocaleUsedForToTitleCase(final Locale locale) {
+ @Nonnull
+ private static Locale getLocaleUsedForToTitleCase(@Nonnull final Locale locale) {
// In Greek locale {@link String#toUpperCase(Locale)} eliminates accents from its result.
// In order to get accented upper case letter, {@link Locale#ROOT} should be used.
if (LANGUAGE_GREEK.equals(locale.getLanguage())) {
@@ -502,25 +597,26 @@ public final class StringUtils {
return locale;
}
- public static String toUpperCaseOfStringForLocale(final String text,
- final boolean needsToUpperCase, final Locale locale) {
- if (text == null || !needsToUpperCase) {
- return text;
+ @Nullable
+ public static String toTitleCaseOfKeyLabel(@Nullable final String label,
+ @Nonnull final Locale locale) {
+ if (label == null) {
+ return label;
}
- return text.toUpperCase(getLocaleUsedForToTitleCase(locale));
+ return label.toUpperCase(getLocaleUsedForToTitleCase(locale));
}
- public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
- 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;
+ public static int toTitleCaseOfKeyCode(final int code, @Nonnull final Locale locale) {
+ if (!Constants.isLetterCode(code)) {
+ return code;
+ }
+ final String label = newSingleCodePointString(code);
+ final String titleCaseLabel = toTitleCaseOfKeyLabel(label, locale);
+ return codePointCount(titleCaseLabel) == 1
+ ? titleCaseLabel.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) {
@@ -529,72 +625,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]);
@@ -602,10 +662,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);
}
@@ -623,9 +682,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/common/src/com/android/inputmethod/latin/common/UnicodeSurrogate.java b/common/src/com/android/inputmethod/latin/common/UnicodeSurrogate.java
new file mode 100644
index 000000000..10974634d
--- /dev/null
+++ b/common/src/com/android/inputmethod/latin/common/UnicodeSurrogate.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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;
+
+/**
+ * Emojis are supplementary characters expressed as a low+high pair. For instance,
+ * the emoji U+1F625 is encoded as "\uD83D\uDE25" in UTF-16, where '\uD83D' is in
+ * the range of [0xd800, 0xdbff] and '\uDE25' is in the range of [0xdc00, 0xdfff].
+ * {@see http://docs.oracle.com/javase/6/docs/api/java/lang/Character.html#unicode}
+ */
+public final class UnicodeSurrogate {
+ private static final char LOW_SURROGATE_MIN = '\uD800';
+ private static final char LOW_SURROGATE_MAX = '\uDBFF';
+ private static final char HIGH_SURROGATE_MIN = '\uDC00';
+ private static final char HIGH_SURROGATE_MAX = '\uDFFF';
+
+ public static boolean isLowSurrogate(final char c) {
+ return c >= LOW_SURROGATE_MIN && c <= LOW_SURROGATE_MAX;
+ }
+
+ public static boolean isHighSurrogate(final char c) {
+ return c >= HIGH_SURROGATE_MIN && c <= HIGH_SURROGATE_MAX;
+ }
+}