aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/values/keypress-vibration-durations.xml2
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java82
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java166
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java102
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java47
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtype.java12
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java2
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java4
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java2
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java8
-rw-r--r--java/src/com/android/inputmethod/latin/NativeSuggestOptions.java13
-rw-r--r--java/src/com/android/inputmethod/latin/ResourceUtils.java81
-rw-r--r--java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java11
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsFragment.java17
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java8
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java90
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java5
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/AccountUtils.java19
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java169
-rw-r--r--java/src/com/android/inputmethod/research/LogUnit.java14
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java33
-rw-r--r--native/jni/src/dictionary.h4
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java (renamed from tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java)62
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java19
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MatrixUtilsTests.java84
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/SmoothingUtilsTests.java45
-rw-r--r--tests/src/com/android/inputmethod/latin/InputPointersTests.java74
-rw-r--r--tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java34
-rw-r--r--tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java84
-rw-r--r--tests/src/com/android/inputmethod/latin/StringUtilsTests.java61
34 files changed, 1010 insertions, 362 deletions
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
index 9ce5051d2..ad6beadb6 100644
--- a/java/res/values/keypress-vibration-durations.xml
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -37,7 +37,7 @@
<item>MODEL=(SAMSUNG-)?GT-I(930[05][NT]?|9308):MANUFACTURER=samsung,8</item>
<item>MODEL=(SAMSUNG-)?SGH-(T999[V]?|I747[M]?|N064|N035):MANUFACTURER=samsung,8</item>
<item>MODEL=(SAMSUNG-)?SCH-(J021|R530|I535|I939):MANUFACTURER=samsung,8</item>
- <item>MODEL=(SAMSUNG-)?(SCL21|SC-06D|SC-03E]):MANUFACTURER=samsung,8</item>
+ <item>MODEL=(SAMSUNG-)?(SCL21|SC-06D|SC-03E):MANUFACTURER=samsung,8</item>
<item>MODEL=(SAMSUNG-)?(SHV-210[KLS]?|SPH-L710):MANUFACTURER=samsung,8</item>
<!-- LG Optimus G -->
<item>MODEL=LG-E97[013]|LS970|L-01E:MANUFACTURER=LGE,15</item>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index c5bd62431..83f109014 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -335,10 +335,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
}
- public boolean isInMomentarySwitchState() {
- return mState.isInMomentarySwitchState();
- }
-
/**
* Updates state machine to figure out when to automatically switch back to the previous mode.
*/
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 34464f690..7493df874 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -315,9 +315,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
default:
final int longpressTimeout =
Settings.getInstance().getCurrent().mKeyLongpressTimeout;
- if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
- // We use longer timeout for sliding finger input started from the symbols
- // mode key.
+ if (tracker.isInSlidingKeyInputFromModifier()) {
+ // We use longer timeout for sliding finger input started from the modifier key.
delay = longpressTimeout * 3;
} else {
delay = longpressTimeout;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index b1813a141..ba449eeb3 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -53,7 +53,9 @@ public final class KeySpecParser {
private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
// Constants for parsing.
- private static final char LABEL_END = '|';
+ private static final char COMMA = ',';
+ private static final char BACKSLASH = '\\';
+ private static final char VERTICAL_BAR = '|';
private static final String PREFIX_TEXT = "!text/";
static final String PREFIX_ICON = "!icon/";
private static final String PREFIX_CODE = "!code/";
@@ -64,6 +66,59 @@ public final class KeySpecParser {
// Intentional empty constructor for utility class.
}
+ /**
+ * Split the text containing multiple key specifications separated by commas into an array of
+ * key specifications.
+ * A key specification can contain a character escaped by the backslash character, including a
+ * comma character.
+ * Note that an empty key specification will be eliminated from the result array.
+ *
+ * @param text the text containing multiple key specifications.
+ * @return an array of key specification text. Null if the specified <code>text</code> is empty
+ * or has no key specifications.
+ */
+ public static String[] splitKeySpecs(final String text) {
+ final int size = text.length();
+ if (size == 0) {
+ return null;
+ }
+ // Optimization for one-letter key specification.
+ if (size == 1) {
+ return text.charAt(0) == COMMA ? null : new String[] { text };
+ }
+
+ ArrayList<String> list = null;
+ int start = 0;
+ // The characters in question in this loop are COMMA and BACKSLASH. These characters never
+ // match any high or low surrogate character. So it is OK to iterate through with char
+ // index.
+ for (int pos = 0; pos < size; pos++) {
+ final char c = text.charAt(pos);
+ if (c == COMMA) {
+ // Skip empty entry.
+ if (pos - start > 0) {
+ if (list == null) {
+ list = CollectionUtils.newArrayList();
+ }
+ list.add(text.substring(start, pos));
+ }
+ // Skip comma
+ start = pos + 1;
+ } else if (c == BACKSLASH) {
+ // Skip escape character and escaped character.
+ pos++;
+ }
+ }
+ final String remain = (size - start > 0) ? text.substring(start) : null;
+ if (list == null) {
+ return remain != null ? new String[] { remain } : null;
+ }
+ if (remain != null) {
+ list.add(remain);
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
private static boolean hasIcon(final String moreKeySpec) {
return moreKeySpec.startsWith(PREFIX_ICON);
}
@@ -78,14 +133,14 @@ public final class KeySpecParser {
}
private static String parseEscape(final String text) {
- if (text.indexOf(Constants.CSV_ESCAPE) < 0) {
+ if (text.indexOf(BACKSLASH) < 0) {
return text;
}
final int length = text.length();
final StringBuilder sb = new StringBuilder();
for (int pos = 0; pos < length; pos++) {
final char c = text.charAt(pos);
- if (c == Constants.CSV_ESCAPE && pos + 1 < length) {
+ if (c == BACKSLASH && pos + 1 < length) {
// Skip escape char
pos++;
sb.append(text.charAt(pos));
@@ -97,20 +152,20 @@ public final class KeySpecParser {
}
private static int indexOfLabelEnd(final String moreKeySpec, final int start) {
- if (moreKeySpec.indexOf(Constants.CSV_ESCAPE, start) < 0) {
- final int end = moreKeySpec.indexOf(LABEL_END, start);
+ if (moreKeySpec.indexOf(BACKSLASH, start) < 0) {
+ final int end = moreKeySpec.indexOf(VERTICAL_BAR, start);
if (end == 0) {
- throw new KeySpecParserError(LABEL_END + " at " + start + ": " + moreKeySpec);
+ throw new KeySpecParserError(VERTICAL_BAR + " at " + start + ": " + moreKeySpec);
}
return end;
}
final int length = moreKeySpec.length();
for (int pos = start; pos < length; pos++) {
final char c = moreKeySpec.charAt(pos);
- if (c == Constants.CSV_ESCAPE && pos + 1 < length) {
+ if (c == BACKSLASH && pos + 1 < length) {
// Skip escape char
pos++;
- } else if (c == LABEL_END) {
+ } else if (c == VERTICAL_BAR) {
return pos;
}
}
@@ -136,9 +191,9 @@ public final class KeySpecParser {
return null;
}
if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
- throw new KeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec);
+ throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + moreKeySpec);
}
- return parseEscape(moreKeySpec.substring(end + /* LABEL_END */1));
+ return parseEscape(moreKeySpec.substring(end + /* VERTICAL_BAR */1));
}
static String getOutputText(final String moreKeySpec) {
@@ -169,7 +224,7 @@ public final class KeySpecParser {
if (hasCode(moreKeySpec)) {
final int end = indexOfLabelEnd(moreKeySpec, 0);
if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
- throw new KeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec);
+ throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + moreKeySpec);
}
return parseCode(moreKeySpec.substring(end + 1), codesSet, CODE_UNSPECIFIED);
}
@@ -204,7 +259,7 @@ public final class KeySpecParser {
public static int getIconId(final String moreKeySpec) {
if (moreKeySpec != null && hasIcon(moreKeySpec)) {
- final int end = moreKeySpec.indexOf(LABEL_END, PREFIX_ICON.length());
+ final int end = moreKeySpec.indexOf(VERTICAL_BAR, PREFIX_ICON.length());
final String name = (end < 0) ? moreKeySpec.substring(PREFIX_ICON.length())
: moreKeySpec.substring(PREFIX_ICON.length(), end);
return KeyboardIconsSet.getIconId(name);
@@ -351,7 +406,7 @@ public final class KeySpecParser {
final String name = text.substring(pos + prefixLen, end);
sb.append(textsSet.getText(name));
pos = end - 1;
- } else if (c == Constants.CSV_ESCAPE) {
+ } else if (c == BACKSLASH) {
if (sb != null) {
// Append both escape character and escaped character.
sb.append(text.substring(pos, Math.min(pos + 2, size)));
@@ -366,7 +421,6 @@ public final class KeySpecParser {
text = sb.toString();
}
} while (sb != null);
-
return text;
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
index 5db3ebbd1..f65056948 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
@@ -18,8 +18,6 @@ package com.android.inputmethod.keyboard.internal;
import android.content.res.TypedArray;
-import com.android.inputmethod.latin.StringUtils;
-
public abstract class KeyStyle {
private final KeyboardTextsSet mTextsSet;
@@ -42,7 +40,7 @@ public abstract class KeyStyle {
protected String[] parseStringArray(final TypedArray a, final int index) {
if (a.hasValue(index)) {
final String text = KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
- return StringUtils.parseCsvString(text);
+ return KeySpecParser.splitKeySpecs(text);
}
return null;
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index f18d5edff..9f6374bf7 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -574,11 +574,6 @@ public final class KeyboardState {
}
}
- public boolean isInMomentarySwitchState() {
- return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL
- || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
- }
-
private static boolean isSpaceCharacter(final int c) {
return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER;
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java b/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java
new file mode 100644
index 000000000..4916a15b5
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/MatrixUtils.java
@@ -0,0 +1,166 @@
+/*
+ * 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 com.android.inputmethod.annotations.UsedForTesting;
+
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Utilities for matrix operations. Don't instantiate objects inside this class to prevent
+ * unexpected performance regressions.
+ */
+@UsedForTesting
+public class MatrixUtils {
+ private static final String TAG = MatrixUtils.class.getSimpleName();
+ public static class MatrixOperationFailedException extends Exception {
+ private static final String TAG = MatrixOperationFailedException.class.getSimpleName();
+ private static final long serialVersionUID = 4384485606788583829L;
+
+ public MatrixOperationFailedException(String msg) {
+ super(msg);
+ Log.d(TAG, msg);
+ }
+ }
+
+ /**
+ * A utility function to inverse matrix.
+ * Find a pivot and swap the row of squareMatrix0 and squareMatrix1
+ */
+ private static void findPivotAndSwapRow(final int row, final float[][] squareMatrix0,
+ final float[][] squareMatrix1, final int size) {
+ int ip = row;
+ float pivot = Math.abs(squareMatrix0[row][row]);
+ for (int i = row + 1; i < size; ++i) {
+ if (pivot < Math.abs(squareMatrix0[i][row])) {
+ ip = i;
+ pivot = Math.abs(squareMatrix0[i][row]);
+ }
+ }
+ if (ip != row) {
+ for (int j = 0; j < size; ++j) {
+ final float temp0 = squareMatrix0[ip][j];
+ squareMatrix0[ip][j] = squareMatrix0[row][j];
+ squareMatrix0[row][j] = temp0;
+ final float temp1 = squareMatrix1[ip][j];
+ squareMatrix1[ip][j] = squareMatrix1[row][j];
+ squareMatrix1[row][j] = temp1;
+ }
+ }
+ }
+
+ /**
+ * A utility function to inverse matrix. This function calculates answer for each row by
+ * sweeping method of Gauss Jordan elimination
+ */
+ private static void sweep(final int row, final float[][] squareMatrix0,
+ final float[][] squareMatrix1, final int size) throws MatrixOperationFailedException {
+ final float pivot = squareMatrix0[row][row];
+ if (pivot == 0) {
+ throw new MatrixOperationFailedException("Inverse failed. Invalid pivot");
+ }
+ for (int j = 0; j < size; ++j) {
+ squareMatrix0[row][j] /= pivot;
+ squareMatrix1[row][j] /= pivot;
+ }
+ for (int i = 0; i < size; i++) {
+ final float sweepTargetValue = squareMatrix0[i][row];
+ if (i != row) {
+ for (int j = row; j < size; ++j) {
+ squareMatrix0[i][j] -= sweepTargetValue * squareMatrix0[row][j];
+ }
+ for (int j = 0; j < size; ++j) {
+ squareMatrix1[i][j] -= sweepTargetValue * squareMatrix1[row][j];
+ }
+ }
+ }
+ }
+
+ /**
+ * A function to inverse matrix.
+ * The inverse matrix of squareMatrix will be output to inverseMatrix. Please notice that
+ * the value of squareMatrix is modified in this function and can't be resuable.
+ */
+ @UsedForTesting
+ public static void inverse(final float[][] squareMatrix,
+ final float[][] inverseMatrix) throws MatrixOperationFailedException {
+ final int size = squareMatrix.length;
+ if (squareMatrix[0].length != size || inverseMatrix.length != size
+ || inverseMatrix[0].length != size) {
+ throw new MatrixOperationFailedException(
+ "--- invalid length. column should be 2 times larger than row.");
+ }
+ for (int i = 0; i < size; ++i) {
+ Arrays.fill(inverseMatrix[i], 0.0f);
+ inverseMatrix[i][i] = 1.0f;
+ }
+ for (int i = 0; i < size; ++i) {
+ findPivotAndSwapRow(i, squareMatrix, inverseMatrix, size);
+ sweep(i, squareMatrix, inverseMatrix, size);
+ }
+ }
+
+ /**
+ * A matrix operation to multiply m0 and m1.
+ */
+ @UsedForTesting
+ public static void multiply(final float[][] m0, final float[][] m1,
+ final float[][] retval) throws MatrixOperationFailedException {
+ if (m0[0].length != m1.length) {
+ throw new MatrixOperationFailedException(
+ "--- invalid length for multiply " + m0[0].length + ", " + m1.length);
+ }
+ final int m0h = m0.length;
+ final int m0w = m0[0].length;
+ final int m1w = m1[0].length;
+ if (retval.length != m0h || retval[0].length != m1w) {
+ throw new MatrixOperationFailedException(
+ "--- invalid length of retval " + retval.length + ", " + retval[0].length);
+ }
+
+ for (int i = 0; i < m0h; i++) {
+ Arrays.fill(retval[i], 0);
+ for (int j = 0; j < m1w; j++) {
+ for (int k = 0; k < m0w; k++) {
+ retval[i][j] += m0[i][k] * m1[k][j];
+ }
+ }
+ }
+ }
+
+ /**
+ * A utility function to dump the specified matrix in a readable way
+ */
+ @UsedForTesting
+ public static void dump(final String title, final float[][] a) {
+ final int column = a[0].length;
+ final int row = a.length;
+ Log.d(TAG, "Dump matrix: " + title);
+ Log.d(TAG, "/*---------------------");
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < row; ++i) {
+ sb.setLength(0);
+ for (int j = 0; j < column; ++j) {
+ sb.append(String.format("%4f", a[i][j])).append(' ');
+ }
+ Log.d(TAG, sb.toString());
+ }
+ Log.d(TAG, "---------------------*/");
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java b/java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java
new file mode 100644
index 000000000..e5665bcdd
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/SmoothingUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.keyboard.internal.MatrixUtils.MatrixOperationFailedException;
+
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Utilities to smooth coordinates. Currently, we calculate 3d least squares formula by using
+ * Lagrangian smoothing
+ */
+@UsedForTesting
+public class SmoothingUtils {
+ private static final String TAG = SmoothingUtils.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private SmoothingUtils() {
+ // not allowed to instantiate publicly
+ }
+
+ /**
+ * Find a most likely 3d least squares formula for specified coordinates.
+ * "retval" should be a 1x4 size matrix.
+ */
+ @UsedForTesting
+ public static void get3DParameters(final float[] xs, final float[] ys,
+ final float[][] retval) throws MatrixOperationFailedException {
+ final int COEFF_COUNT = 4; // Coefficient count for 3d smoothing
+ if (retval.length != COEFF_COUNT || retval[0].length != 1) {
+ Log.d(TAG, "--- invalid length of 3d retval " + retval.length + ", "
+ + retval[0].length);
+ return;
+ }
+ final int N = xs.length;
+ // TODO: Never isntantiate the matrix
+ final float[][] m0 = new float[COEFF_COUNT][COEFF_COUNT];
+ final float[][] m0Inv = new float[COEFF_COUNT][COEFF_COUNT];
+ final float[][] m1 = new float[COEFF_COUNT][N];
+ final float[][] m2 = new float[N][1];
+
+ // m0
+ for (int i = 0; i < COEFF_COUNT; ++i) {
+ Arrays.fill(m0[i], 0);
+ for (int j = 0; j < COEFF_COUNT; ++j) {
+ final int pow = i + j;
+ for (int k = 0; k < N; ++k) {
+ m0[i][j] += (float) Math.pow((double) xs[k], pow);
+ }
+ }
+ }
+ // m0Inv
+ MatrixUtils.inverse(m0, m0Inv);
+ if (DEBUG) {
+ MatrixUtils.dump("m0-1", m0Inv);
+ }
+
+ // m1
+ for (int i = 0; i < COEFF_COUNT; ++i) {
+ for (int j = 0; j < N; ++j) {
+ m1[i][j] = (i == 0) ? 1.0f : m1[i - 1][j] * xs[j];
+ }
+ }
+
+ // m2
+ for (int i = 0; i < N; ++i) {
+ m2[i][0] = ys[i];
+ }
+
+ final float[][] m0Invxm1 = new float[COEFF_COUNT][N];
+ if (DEBUG) {
+ MatrixUtils.dump("a0", m0Inv);
+ MatrixUtils.dump("a1", m1);
+ }
+ MatrixUtils.multiply(m0Inv, m1, m0Invxm1);
+ if (DEBUG) {
+ MatrixUtils.dump("a2", m0Invxm1);
+ MatrixUtils.dump("a3", m2);
+ }
+ MatrixUtils.multiply(m0Invxm1, m2, retval);
+ if (DEBUG) {
+ MatrixUtils.dump("result", retval);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java b/java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java
new file mode 100644
index 000000000..0fdaea50c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/AdditionalFeaturesSettingUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethodcommon.InputMethodSettingsFragment;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * Utility class for managing additional features settings.
+ */
+public class AdditionalFeaturesSettingUtils {
+ public static final int ADDITIONAL_FEATURES_SETTINGS_SIZE = 0;
+
+ private AdditionalFeaturesSettingUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public static void addAdditionalFeaturesPreferences(
+ final Context context, final InputMethodSettingsFragment settingsFragment) {
+ // do nothing.
+ }
+
+ public static void readAdditionalFeaturesPreferencesIntoArray(
+ final SharedPreferences prefs, final int[] additionalFeaturesPreferences) {
+ // do nothing.
+ }
+
+ public static int[] getAdditionalNativeSuggestOptions() {
+ return Settings.getInstance().getCurrent().mAdditionalFeaturesSettingValues;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index 99b95ea98..85b14d849 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -49,13 +49,14 @@ public final class AdditionalSubtype {
&& SubtypeLocale.isExceptionalLocale(localeString)) {
final String layoutDisplayName = SubtypeLocale.getKeyboardLayoutSetDisplayName(
keyboardLayoutSetName);
- layoutDisplayNameExtraValue = StringUtils.appendToCsvIfNotExists(
+ layoutDisplayNameExtraValue = StringUtils.appendToCommaSplittableTextIfNotExists(
UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" + layoutDisplayName, extraValue);
} else {
layoutDisplayNameExtraValue = extraValue;
}
- final String additionalSubtypeExtraValue = StringUtils.appendToCsvIfNotExists(
- IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
+ final String additionalSubtypeExtraValue =
+ StringUtils.appendToCommaSplittableTextIfNotExists(
+ IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
final int nameId = SubtypeLocale.getSubtypeNameId(localeString, keyboardLayoutSetName);
return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
localeString, KEYBOARD_MODE,
@@ -66,8 +67,9 @@ public final class AdditionalSubtype {
final String localeString = subtype.getLocale();
final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
- final String extraValue = StringUtils.removeFromCsvIfExists(layoutExtraValue,
- StringUtils.removeFromCsvIfExists(IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
+ final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists(
+ layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists(
+ IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
final String basePrefSubtype = localeString + LOCALE_AND_LAYOUT_SEPARATOR
+ keyboardLayoutSetName;
return extraValue.isEmpty() ? basePrefSubtype
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index c644a7722..aad129d76 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -136,6 +136,8 @@ public final class BinaryDictionary extends Dictionary {
final InputPointers ips = composer.getInputPointers();
final int inputSize = isGesture ? ips.getPointerSize() : composerSize;
mNativeSuggestOptions.setIsGesture(isGesture);
+ mNativeSuggestOptions.setAdditionalFeaturesOptions(
+ AdditionalFeaturesSettingUtils.getAdditionalNativeSuggestOptions());
// proximityInfo and/or prevWordForBigrams may not be null.
final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
getTraverseSession(sessionId).getSession(), ips.getXCoordinates(),
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 86bb25562..64c14d32f 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -215,10 +215,6 @@ public final class Constants {
}
}
- // Constants for CSV parsing.
- public static final char CSV_SEPARATOR = ',';
- public static final char CSV_ESCAPE = '\\';
-
private Constants() {
// This utility class is not publicly instantiable.
}
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index dd58db575..1f673e9b0 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -199,6 +199,6 @@ public final class InputAttributes {
if (editorInfo == null) return false;
final String findingKey = (packageName != null) ? packageName + "." + key
: key;
- return StringUtils.containsInCsv(findingKey, editorInfo.privateImeOptions);
+ return StringUtils.containsInCommaSplittableText(findingKey, editorInfo.privateImeOptions);
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 592db35dd..cebc93c18 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -2369,9 +2369,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
final boolean showingAddToDictionaryHint =
- SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind && mSuggest != null
- // If the suggestion is not in the dictionary, the hint should be shown.
- && !AutoCorrection.isValidWord(mSuggest, suggestion, true);
+ (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
+ || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
+ && mSuggest != null
+ // If the suggestion is not in the dictionary, the hint should be shown.
+ && !AutoCorrection.isValidWord(mSuggest, suggestion, true);
if (mSettings.isInternal()) {
Stats.onSeparator((char)Constants.CODE_SPACE,
diff --git a/java/src/com/android/inputmethod/latin/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/NativeSuggestOptions.java
index 4425f07b7..291551301 100644
--- a/java/src/com/android/inputmethod/latin/NativeSuggestOptions.java
+++ b/java/src/com/android/inputmethod/latin/NativeSuggestOptions.java
@@ -22,7 +22,8 @@ public class NativeSuggestOptions {
private static final int USE_FULL_EDIT_DISTANCE = 1;
private static final int OPTIONS_SIZE = 2;
- private final int[] mOptions = new int[OPTIONS_SIZE];
+ private final int[] mOptions = new int[OPTIONS_SIZE
+ + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
public void setIsGesture(final boolean value) {
setBooleanOption(IS_GESTURE, value);
@@ -32,6 +33,12 @@ public class NativeSuggestOptions {
setBooleanOption(USE_FULL_EDIT_DISTANCE, value);
}
+ public void setAdditionalFeaturesOptions(final int[] additionalOptions) {
+ for (int i = 0; i < additionalOptions.length; i++) {
+ setIntegerOption(OPTIONS_SIZE + i, additionalOptions[i]);
+ }
+ }
+
public int[] getOptions() {
return mOptions;
}
@@ -39,4 +46,8 @@ public class NativeSuggestOptions {
private void setBooleanOption(final int key, final boolean value) {
mOptions[key] = value ? 1 : 0;
}
+
+ private void setIntegerOption(final int key, final int value) {
+ mOptions[key] = value;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java
index a9fba5348..0eb8b4f09 100644
--- a/java/src/com/android/inputmethod/latin/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java
@@ -27,6 +27,7 @@ import com.android.inputmethod.annotations.UsedForTesting;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.regex.PatternSyntaxException;
public final class ResourceUtils {
private static final String TAG = ResourceUtils.class.getSimpleName();
@@ -83,22 +84,39 @@ public final class ResourceUtils {
return overrideValue;
}
- final String defaultValue = findDefaultConstant(overrideArray);
- // The defaultValue might be an empty string.
- if (defaultValue == null) {
- Log.w(TAG, "Couldn't find override value nor default value:"
- + " resource="+ res.getResourceEntryName(overrideResId)
- + " build=" + sBuildKeyValuesDebugString);
- } else {
- Log.i(TAG, "Found default value:"
- + " resource="+ res.getResourceEntryName(overrideResId)
- + " build=" + sBuildKeyValuesDebugString
- + " default=" + defaultValue);
+ String defaultValue = null;
+ try {
+ defaultValue = findDefaultConstant(overrideArray);
+ // The defaultValue might be an empty string.
+ if (defaultValue == null) {
+ Log.w(TAG, "Couldn't find override value nor default value:"
+ + " resource="+ res.getResourceEntryName(overrideResId)
+ + " build=" + sBuildKeyValuesDebugString);
+ } else {
+ Log.i(TAG, "Found default value:"
+ + " resource="+ res.getResourceEntryName(overrideResId)
+ + " build=" + sBuildKeyValuesDebugString
+ + " default=" + defaultValue);
+ }
+ } catch (final DeviceOverridePatternSyntaxError e) {
+ Log.w(TAG, "Syntax error, ignored", e);
}
sDeviceOverrideValueMap.put(key, defaultValue);
return defaultValue;
}
+ @SuppressWarnings("serial")
+ static class DeviceOverridePatternSyntaxError extends Exception {
+ public DeviceOverridePatternSyntaxError(final String message, final String expression) {
+ this(message, expression, null);
+ }
+
+ public DeviceOverridePatternSyntaxError(final String message, final String expression,
+ final Throwable throwable) {
+ super(message + ": " + expression, throwable);
+ }
+ }
+
/**
* Find the condition that fulfills specified key value pairs from an array of
* "condition,constant", and return the corresponding string constant. A condition is
@@ -123,10 +141,12 @@ public final class ResourceUtils {
if (conditionConstantArray == null || keyValuePairs == null) {
return null;
}
+ String foundValue = null;
for (final String conditionConstant : conditionConstantArray) {
final int posComma = conditionConstant.indexOf(',');
if (posComma < 0) {
- throw new RuntimeException("Array element has no comma: " + conditionConstant);
+ Log.w(TAG, "Array element has no comma: " + conditionConstant);
+ continue;
}
final String condition = conditionConstant.substring(0, posComma);
if (condition.isEmpty()) {
@@ -134,44 +154,59 @@ public final class ResourceUtils {
// {@link #findConstantForDefault(String[])}.
continue;
}
- if (fulfillsCondition(keyValuePairs, condition)) {
- return conditionConstant.substring(posComma + 1);
+ try {
+ if (fulfillsCondition(keyValuePairs, condition)) {
+ // Take first match
+ if (foundValue == null) {
+ foundValue = conditionConstant.substring(posComma + 1);
+ }
+ // And continue walking through all conditions.
+ }
+ } catch (final DeviceOverridePatternSyntaxError e) {
+ Log.w(TAG, "Syntax error, ignored", e);
}
}
- return null;
+ return foundValue;
}
private static boolean fulfillsCondition(final HashMap<String,String> keyValuePairs,
- final String condition) {
+ final String condition) throws DeviceOverridePatternSyntaxError {
final String[] patterns = condition.split(":");
// Check all patterns in a condition are true
+ boolean matchedAll = true;
for (final String pattern : patterns) {
final int posEqual = pattern.indexOf('=');
if (posEqual < 0) {
- throw new RuntimeException("Pattern has no '=': " + condition);
+ throw new DeviceOverridePatternSyntaxError("Pattern has no '='", condition);
}
final String key = pattern.substring(0, posEqual);
final String value = keyValuePairs.get(key);
if (value == null) {
- throw new RuntimeException("Found unknown key: " + condition);
+ throw new DeviceOverridePatternSyntaxError("Unknown key", condition);
}
final String patternRegexpValue = pattern.substring(posEqual + 1);
- if (!value.matches(patternRegexpValue)) {
- return false;
+ try {
+ if (!value.matches(patternRegexpValue)) {
+ matchedAll = false;
+ // And continue walking through all patterns.
+ }
+ } catch (final PatternSyntaxException e) {
+ throw new DeviceOverridePatternSyntaxError("Syntax error", condition, e);
}
}
- return true;
+ return matchedAll;
}
@UsedForTesting
- static String findDefaultConstant(final String[] conditionConstantArray) {
+ static String findDefaultConstant(final String[] conditionConstantArray)
+ throws DeviceOverridePatternSyntaxError {
if (conditionConstantArray == null) {
return null;
}
for (final String condition : conditionConstantArray) {
final int posComma = condition.indexOf(',');
if (posComma < 0) {
- throw new RuntimeException("Array element has no comma: " + condition);
+ throw new DeviceOverridePatternSyntaxError("Array element has no comma", condition);
}
if (posComma == 0) { // condition is empty.
return condition.substring(posComma + 1);
diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
index 7c4156c48..3ea9fedd7 100644
--- a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
+++ b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
@@ -32,6 +32,7 @@ public final class SeekBarDialogPreference extends DialogPreference
public int readValue(final String key);
public int readDefaultValue(final String key);
public void writeValue(final int value, final String key);
+ public void writeDefaultValue(final String key);
public void feedbackValue(final int value);
}
@@ -122,12 +123,16 @@ public final class SeekBarDialogPreference extends DialogPreference
@Override
public void onClick(final DialogInterface dialog, final int which) {
super.onClick(dialog, which);
+ final String key = getKey();
if (which == DialogInterface.BUTTON_NEUTRAL) {
- setValue(clipValue(mValueProxy.readDefaultValue(getKey())), false /* fromUser */);
+ setValue(clipValue(mValueProxy.readDefaultValue(key)), false /* fromUser */);
+ mValueProxy.writeDefaultValue(key);
+ return;
}
- if (which != DialogInterface.BUTTON_NEGATIVE) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
setSummary(mValueView.getText());
- mValueProxy.writeValue(getClippedValueFromProgress(mSeekBar.getProgress()), getKey());
+ mValueProxy.writeValue(getClippedValueFromProgress(mSeekBar.getProgress()), key);
+ return;
}
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index 835ef7b46..7225cd6bf 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -207,6 +207,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment
if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) {
removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen());
+ } else {
+ AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this);
}
setupKeyLongpressTimeoutSettings(prefs, res);
@@ -327,6 +329,11 @@ public final class SettingsFragment extends InputMethodSettingsFragment
}
@Override
+ public void writeDefaultValue(final String key) {
+ sp.edit().remove(key).apply();
+ }
+
+ @Override
public int readValue(final String key) {
return Settings.readKeypressVibrationDuration(sp, res);
}
@@ -357,6 +364,11 @@ public final class SettingsFragment extends InputMethodSettingsFragment
}
@Override
+ public void writeDefaultValue(final String key) {
+ sp.edit().remove(key).apply();
+ }
+
+ @Override
public int readValue(final String key) {
return Settings.readKeyLongpressTimeout(sp, res);
}
@@ -395,6 +407,11 @@ public final class SettingsFragment extends InputMethodSettingsFragment
}
@Override
+ public void writeDefaultValue(final String key) {
+ sp.edit().remove(key).apply();
+ }
+
+ @Override
public int readValue(final String key) {
return getPercentageFromValue(Settings.readKeypressSoundVolume(sp, res));
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 615b2dfab..09102447f 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -80,6 +80,10 @@ public final class SettingsValues {
private final boolean mVoiceKeyEnabled;
private final boolean mVoiceKeyOnMain;
+ // Setting values for additional features
+ public final int[] mAdditionalFeaturesSettingValues =
+ new int[AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
+
// Debug settings
public final boolean mIsInternal;
@@ -96,7 +100,7 @@ public final class SettingsValues {
mWordConnectors =
StringUtils.toCodePointArray(res.getString(R.string.symbols_word_connectors));
Arrays.sort(mWordConnectors);
- final String[] suggestPuncsSpec = StringUtils.parseCsvString(res.getString(
+ final String[] suggestPuncsSpec = KeySpecParser.splitKeySpecs(res.getString(
R.string.suggested_punctuations));
mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
mWordSeparators = res.getString(R.string.symbols_word_separators);
@@ -149,6 +153,8 @@ public final class SettingsValues {
Settings.PREF_SHOW_SUGGESTIONS_SETTING,
res.getString(R.string.prefs_suggestion_visibility_default_value));
mSuggestionVisibility = createSuggestionVisibility(res, showSuggestionsSetting);
+ AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray(
+ prefs, mAdditionalFeaturesSettingValues);
mIsInternal = Settings.isInternal(prefs);
}
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index ab050d7a3..c2fd4fb32 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -35,33 +35,55 @@ public final class StringUtils {
return text.codePointCount(0, text.length());
}
- public static boolean containsInArray(final String key, final String[] array) {
+ public static boolean containsInArray(final String text, final String[] array) {
for (final String element : array) {
- if (key.equals(element)) return true;
+ if (text.equals(element)) return true;
}
return false;
}
- public static boolean containsInCsv(final String key, final String csv) {
- if (TextUtils.isEmpty(csv)) return false;
- return containsInArray(key, csv.split(","));
+ /**
+ * Comma-Splittable Text is similar to Comma-Separated Values (CSV) but has much simpler syntax.
+ * Unlike CSV, Comma-Splittable Text has no escaping mechanism, so that the text can't contain
+ * a comma character in it.
+ */
+ private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ",";
+
+ public static boolean containsInCommaSplittableText(final String text,
+ final String extraValues) {
+ if (TextUtils.isEmpty(extraValues)) {
+ return false;
+ }
+ return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT));
}
- public static String appendToCsvIfNotExists(final String key, final String csv) {
- if (TextUtils.isEmpty(csv)) return key;
- if (containsInCsv(key, csv)) return csv;
- return csv + "," + key;
+ public static String appendToCommaSplittableTextIfNotExists(final String text,
+ final String extraValues) {
+ if (TextUtils.isEmpty(extraValues)) {
+ return text;
+ }
+ if (containsInCommaSplittableText(text, extraValues)) {
+ return extraValues;
+ }
+ return extraValues + SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT + text;
}
- public static String removeFromCsvIfExists(final String key, final String csv) {
- if (TextUtils.isEmpty(csv)) return "";
- final String[] elements = csv.split(",");
- if (!containsInArray(key, elements)) return csv;
+ public static String removeFromCommaSplittableTextIfExists(final String text,
+ final String extraValues) {
+ if (TextUtils.isEmpty(extraValues)) {
+ return "";
+ }
+ final String[] elements = extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT);
+ if (!containsInArray(text, elements)) {
+ return extraValues;
+ }
final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1);
for (final String element : elements) {
- if (!key.equals(element)) result.add(element);
+ if (!text.equals(element)) {
+ result.add(element);
+ }
}
- return TextUtils.join(",", result);
+ return TextUtils.join(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT, result);
}
/**
@@ -131,44 +153,6 @@ public final class StringUtils {
return codePoints;
}
- public static String[] parseCsvString(final String text) {
- final int size = text.length();
- if (size == 0) {
- return null;
- }
- if (codePointCount(text) == 1) {
- return text.codePointAt(0) == Constants.CSV_SEPARATOR ? null : new String[] { text };
- }
-
- ArrayList<String> list = null;
- int start = 0;
- for (int pos = 0; pos < size; pos++) {
- final char c = text.charAt(pos);
- if (c == Constants.CSV_SEPARATOR) {
- // Skip empty entry.
- if (pos - start > 0) {
- if (list == null) {
- list = CollectionUtils.newArrayList();
- }
- list.add(text.substring(start, pos));
- }
- // Skip comma
- start = pos + 1;
- } else if (c == Constants.CSV_ESCAPE) {
- // Skip escape character and escaped character.
- pos++;
- }
- }
- final String remain = (size - start > 0) ? text.substring(start) : null;
- if (list == null) {
- return remain != null ? new String[] { remain } : null;
- }
- if (remain != null) {
- list.add(remain);
- }
- return list.toArray(new String[list.size()]);
- }
-
// This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE.
public static int getCapitalizationType(final String text) {
// If the first char is not uppercase, then the word is either all lower case or
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index dfddb0ffe..1f453273b 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -132,7 +132,10 @@ public final class SuggestedWords {
public static final int KIND_APP_DEFINED = 6; // Suggested by the application
public static final int KIND_SHORTCUT = 7; // A shortcut
public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
- public static final int KIND_RESUMED = 9; // A resumed suggestion (comes from a span)
+ // KIND_RESUMED: A resumed suggestion (comes from a span, currently this type is used only
+ // in java for re-correction)
+ public static final int KIND_RESUMED = 9;
+ public static final int KIND_OOV_CORRECTION = 10; // Most probable string correction
public static final int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
index 93687e193..a446672cb 100644
--- a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
+++ b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
@@ -23,6 +23,7 @@ import android.util.Patterns;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
public class AccountUtils {
private AccountUtils() {
@@ -44,4 +45,22 @@ public class AccountUtils {
}
return retval;
}
+
+ /**
+ * Get all device accounts having specified domain name.
+ * @param context application context
+ * @param domain domain name used for filtering
+ * @return List of account names that contain the specified domain name
+ */
+ public static List<String> getDeviceAccountsWithDomain(
+ final Context context, final String domain) {
+ final ArrayList<String> retval = new ArrayList<String>();
+ final String atDomain = "@" + domain.toLowerCase(Locale.ROOT);
+ for (final Account account : getAccounts(context)) {
+ if (account.name.toLowerCase(Locale.ROOT).endsWith(atDomain)) {
+ retval.add(account.name);
+ }
+ }
+ return retval;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 1113939d1..9764610b3 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -250,10 +250,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
- final int pos) {
- final String word = suggestedWords.getWord(pos);
- final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
- final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid;
+ final int indexInSuggestedWords) {
+ final String word = suggestedWords.getWord(indexInSuggestedWords);
+ final boolean isAutoCorrect = indexInSuggestedWords == 1
+ && suggestedWords.willAutoCorrect();
+ final boolean isTypedWordValid = indexInSuggestedWords == 0
+ && suggestedWords.mTypedWordValid;
if (!isAutoCorrect && !isTypedWordValid)
return word;
@@ -270,28 +272,31 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return spannedWord;
}
- private int getWordPosition(final int index, final SuggestedWords suggestedWords) {
+ private int getIndexInSuggestedWords(final int indexInStrip,
+ final SuggestedWords suggestedWords) {
// TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
// suggestions.
- final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0;
- if (index == mCenterSuggestionIndex) {
- return centerPos;
- } else if (index == centerPos) {
+ final int mostImportantIndexInSuggestedWords = suggestedWords.willAutoCorrect() ? 1 : 0;
+ if (indexInStrip == mCenterSuggestionIndex) {
+ return mostImportantIndexInSuggestedWords;
+ } else if (indexInStrip == mostImportantIndexInSuggestedWords) {
return mCenterSuggestionIndex;
} else {
- return index;
+ return indexInStrip;
}
}
- private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords,
- final int pos) {
+ private int getSuggestionTextColor(final int indexInStrip,
+ final SuggestedWords suggestedWords) {
+ final int indexInSuggestedWords = getIndexInSuggestedWords(
+ indexInStrip, suggestedWords);
// TODO: Need to revisit this logic with bigram suggestions
- final boolean isSuggested = (pos != 0);
+ final boolean isSuggested = (indexInSuggestedWords != 0);
final int color;
- if (index == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) {
+ if (indexInStrip == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) {
color = mColorAutoCorrect;
- } else if (index == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) {
+ } else if (indexInStrip == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) {
color = mColorValidTypedWord;
} else if (isSuggested) {
color = mColorSuggested;
@@ -301,7 +306,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
// If we auto-correct, then the autocorrection is in slot 0 and the typed word
// is in slot 1.
- if (index == mCenterSuggestionIndex
+ if (indexInStrip == mCenterSuggestionIndex
&& AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
suggestedWords.getWord(1), suggestedWords.getWord(0))) {
return 0xFFFF0000;
@@ -338,67 +343,101 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
setupTexts(suggestedWords, countInStrip);
mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
int x = 0;
- for (int index = 0; index < countInStrip; index++) {
- final int pos = getWordPosition(index, suggestedWords);
-
- if (index != 0) {
- final View divider = mDividers.get(pos);
+ for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) {
+ if (indexInStrip != 0) {
+ final View divider = mDividers.get(indexInStrip);
// Add divider if this isn't the left most suggestion in suggestions strip.
addDivider(stripView, divider);
x += divider.getMeasuredWidth();
}
- final CharSequence styled = mTexts.get(pos);
- final TextView word = mWords.get(pos);
- if (index == mCenterSuggestionIndex && mMoreSuggestionsAvailable) {
- // TODO: This "more suggestions hint" should have nicely designed icon.
- word.setCompoundDrawablesWithIntrinsicBounds(
- null, null, null, mMoreSuggestionsHint);
- // HACK: To align with other TextView that has no compound drawables.
- word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight());
- } else {
- word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
- }
-
- // Disable this suggestion if the suggestion is null or empty.
- word.setEnabled(!TextUtils.isEmpty(styled));
- word.setTextColor(getSuggestionTextColor(index, suggestedWords, pos));
- final int width = getSuggestionWidth(index, stripWidth);
- final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
- final float scaleX = word.getTextScaleX();
- word.setText(text); // TextView.setText() resets text scale x to 1.0.
- word.setTextScaleX(scaleX);
+ final int width = getSuggestionWidth(indexInStrip, stripWidth);
+ final TextView word = layoutWord(suggestedWords, indexInStrip, width);
stripView.addView(word);
- setLayoutWeight(
- word, getSuggestionWeight(index), ViewGroup.LayoutParams.MATCH_PARENT);
+ setLayoutWeight(word, getSuggestionWeight(indexInStrip),
+ ViewGroup.LayoutParams.MATCH_PARENT);
x += word.getMeasuredWidth();
- if (DBG && pos < suggestedWords.size()) {
- final String debugInfo = Utils.getDebugInfo(suggestedWords, pos);
- if (debugInfo != null) {
- final TextView info = mInfos.get(pos);
- info.setText(debugInfo);
- placer.addView(info);
- info.measure(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- final int infoWidth = info.getMeasuredWidth();
- final int y = info.getMeasuredHeight();
- ViewLayoutUtils.placeViewAt(
- info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
- }
+ if (DBG) {
+ layoutDebugInfo(suggestedWords, indexInStrip, placer, x);
}
}
}
- private int getSuggestionWidth(final int index, final int maxWidth) {
+ /**
+ * Format appropriately the suggested word indirectly specified by
+ * <code>indexInStrip</code> as text in a corresponding {@link TextView}. When the
+ * suggested word doesn't exist, the corresponding {@link TextView} will be disabled
+ * and never respond to user interaction. The suggested word may be shrunk or ellipsized to
+ * fit in the specified width.
+ *
+ * The <code>indexInStrip</code> argument is the index in the suggestion strip. The indices
+ * increase towards the right for LTR scripts and the left for RTL scripts, starting with 0.
+ * The index of the most important suggestion is in {@link #mCenterSuggestionIndex}. This
+ * usually doesn't match the index in <code>suggedtedWords</code> -- see
+ * {@link #getIndexInSuggestedWords(int,SuggestedWords)}.
+ *
+ * @param suggestedWords the list of suggestions.
+ * @param indexInStrip the in the suggestion strip.
+ * @param width the maximum width for layout in pixels.
+ * @return the {@link TextView} containing the suggested word appropriately formatted.
+ */
+ private TextView layoutWord(final SuggestedWords suggestedWords, final int indexInStrip,
+ final int width) {
+ final int indexInSuggestedWords = getIndexInSuggestedWords(
+ indexInStrip, suggestedWords);
+ final CharSequence styled = mTexts.get(indexInSuggestedWords);
+ final TextView word = mWords.get(indexInSuggestedWords);
+ if (indexInStrip == mCenterSuggestionIndex && mMoreSuggestionsAvailable) {
+ // TODO: This "more suggestions hint" should have a nicely designed icon.
+ word.setCompoundDrawablesWithIntrinsicBounds(
+ null, null, null, mMoreSuggestionsHint);
+ // HACK: Align with other TextViews that have no compound drawables.
+ word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight());
+ } else {
+ word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+ }
+
+ // Disable this suggestion if the suggestion is null or empty.
+ word.setEnabled(!TextUtils.isEmpty(styled));
+ word.setTextColor(getSuggestionTextColor(indexInStrip, suggestedWords));
+ final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
+ final float scaleX = word.getTextScaleX();
+ word.setText(text); // TextView.setText() resets text scale x to 1.0.
+ word.setTextScaleX(scaleX);
+ return word;
+ }
+
+ private void layoutDebugInfo(final SuggestedWords suggestedWords, final int indexInStrip,
+ final ViewGroup placer, final int x) {
+ final int indexInSuggestedWords = getIndexInSuggestedWords(
+ indexInStrip, suggestedWords);
+ if (indexInSuggestedWords >= suggestedWords.size()) {
+ return;
+ }
+ final String debugInfo = Utils.getDebugInfo(suggestedWords, indexInSuggestedWords);
+ if (debugInfo == null) {
+ return;
+ }
+ final TextView info = mInfos.get(indexInSuggestedWords);
+ info.setText(debugInfo);
+ placer.addView(info);
+ info.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ final int infoWidth = info.getMeasuredWidth();
+ final int y = info.getMeasuredHeight();
+ ViewLayoutUtils.placeViewAt(
+ info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
+ }
+
+ private int getSuggestionWidth(final int indexInStrip, final int maxWidth) {
final int paddings = mPadding * mSuggestionsCountInStrip;
final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
final int availableWidth = maxWidth - paddings - dividers;
- return (int)(availableWidth * getSuggestionWeight(index));
+ return (int)(availableWidth * getSuggestionWeight(indexInStrip));
}
- private float getSuggestionWeight(final int index) {
- if (index == mCenterSuggestionIndex) {
+ private float getSuggestionWeight(final int indexInStrip) {
+ if (indexInStrip == mCenterSuggestionIndex) {
return mCenterSuggestionWeight;
} else {
// TODO: Revisit this for cases of 5 or more suggestions
@@ -422,16 +461,16 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
final ViewGroup stripView) {
final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
- for (int index = 0; index < countInStrip; index++) {
- if (index != 0) {
+ for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) {
+ if (indexInStrip != 0) {
// Add divider if this isn't the left most suggestion in suggestions strip.
- addDivider(stripView, mDividers.get(index));
+ addDivider(stripView, mDividers.get(indexInStrip));
}
- final TextView word = mWords.get(index);
+ final TextView word = mWords.get(indexInStrip);
word.setEnabled(true);
word.setTextColor(mColorAutoCorrect);
- final String text = suggestedWords.getWord(index);
+ final String text = suggestedWords.getWord(indexInStrip);
word.setText(text);
word.setTextScaleX(1.0f);
word.setCompoundDrawables(null, null, null, null);
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
index cf1388f46..164c7e8cc 100644
--- a/java/src/com/android/inputmethod/research/LogUnit.java
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -67,7 +67,7 @@ public class LogUnit {
private String[] mWordArray = EMPTY_STRING_ARRAY;
private boolean mMayContainDigit;
private boolean mIsPartOfMegaword;
- private boolean mContainsCorrection;
+ private boolean mContainsUserDeletions;
// mCorrectionType indicates whether the word was corrected at all, and if so, the nature of the
// correction.
@@ -277,13 +277,13 @@ public class LogUnit {
}
// TODO: Refactor to eliminate getter/setters
- public void setContainsCorrection() {
- mContainsCorrection = true;
+ public void setContainsUserDeletions() {
+ mContainsUserDeletions = true;
}
// TODO: Refactor to eliminate getter/setters
- public boolean containsCorrection() {
- return mContainsCorrection;
+ public boolean containsUserDeletions() {
+ return mContainsUserDeletions;
}
// TODO: Refactor to eliminate getter/setters
@@ -323,7 +323,7 @@ public class LogUnit {
true /* isPartOfMegaword */);
newLogUnit.mWords = null;
newLogUnit.mMayContainDigit = mMayContainDigit;
- newLogUnit.mContainsCorrection = mContainsCorrection;
+ newLogUnit.mContainsUserDeletions = mContainsUserDeletions;
// Purge the logStatements and associated data from this LogUnit.
laterLogStatements.clear();
@@ -346,7 +346,7 @@ public class LogUnit {
setWords(logUnit.mWords);
}
mMayContainDigit = mMayContainDigit || logUnit.mMayContainDigit;
- mContainsCorrection = mContainsCorrection || logUnit.mContainsCorrection;
+ mContainsUserDeletions = mContainsUserDeletions || logUnit.mContainsUserDeletions;
mIsPartOfMegaword = false;
}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 64f0349fc..56ab90cb4 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -260,14 +260,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (DEBUG) {
final String wordsString = logUnit.getWordsAsString();
Log.d(TAG, "onPublish: '" + wordsString
- + "', hc: " + logUnit.containsCorrection()
+ + "', hc: " + logUnit.containsUserDeletions()
+ ", cipd: " + canIncludePrivateData);
}
for (final String word : logUnit.getWordsAsStringArray()) {
final Dictionary dictionary = getDictionary();
mStatistics.recordWordEntered(
dictionary != null && dictionary.isValidWord(word),
- logUnit.containsCorrection());
+ logUnit.containsUserDeletions());
}
}
publishLogUnits(logUnits, mMainResearchLog, canIncludePrivateData);
@@ -819,8 +819,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mCurrentLogUnit.setMayContainDigit();
}
- private void setCurrentLogUnitContainsCorrection() {
- mCurrentLogUnit.setContainsCorrection();
+ private void setCurrentLogUnitContainsUserDeletions() {
+ mCurrentLogUnit.setContainsUserDeletions();
}
private void setCurrentLogUnitCorrectionType(final int correctionType) {
@@ -920,7 +920,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (DEBUG) {
Log.d(TAG, "publishLogBuffer: " + (logUnit.hasOneOrMoreWords()
? logUnit.getWordsAsString() : "<wordless>")
- + ", correction?: " + logUnit.containsCorrection());
+ + ", correction?: " + logUnit.containsUserDeletions());
}
researchLog.publish(logUnit, canIncludePrivateData);
}
@@ -1286,7 +1286,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final ResearchLogger researchLogger = getInstance();
if (!replacedWord.equals(suggestion.toString())) {
// The user chose something other than what was already there.
- researchLogger.setCurrentLogUnitContainsCorrection();
+ researchLogger.setCurrentLogUnitContainsUserDeletions();
researchLogger.setCurrentLogUnitCorrectionType(LogUnit.CORRECTIONTYPE_TYPO);
}
final String scrubbedWord = scrubDigitsFromString(suggestion);
@@ -1463,7 +1463,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
LOGSTATEMENT_LATINIME_REVERTCOMMIT, committedWord, originallyTypedWord,
separatorString);
if (logUnit != null) {
- logUnit.setContainsCorrection();
+ logUnit.setContainsUserDeletions();
}
researchLogger.mStatistics.recordRevertCommit(SystemClock.uptimeMillis());
}
@@ -1618,25 +1618,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
private boolean isExpectingCommitText = false;
- /**
- * Log a call to (UnknownClass).commitPartialText
- *
- * SystemResponse: The IME is committing part of a word. This happens if a space is
- * automatically inserted to split a single typed string into two or more words.
- */
- // TODO: This method is currently unused. Find where it should be called from in the IME and
- // add invocations.
- private static final LogStatement LOGSTATEMENT_COMMIT_PARTIAL_TEXT =
- new LogStatement("CommitPartialText", true, false, "newCursorPosition");
- public static void commitPartialText(final String committedWord,
- final long lastTimestampOfWordData, final boolean isBatchMode) {
- final ResearchLogger researchLogger = getInstance();
- final String scrubbedWord = scrubDigitsFromString(committedWord);
- researchLogger.enqueueEvent(LOGSTATEMENT_COMMIT_PARTIAL_TEXT);
- researchLogger.mStatistics.recordAutoCorrection(SystemClock.uptimeMillis());
- researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, lastTimestampOfWordData,
- isBatchMode);
- }
/**
* Log a call to RichInputConnection.commitText().
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index 2ad5b6c0b..edec83fbf 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -41,6 +41,10 @@ class Dictionary {
static const int KIND_APP_DEFINED = 6; // Suggested by the application
static const int KIND_SHORTCUT = 7; // A shortcut
static const int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
+ // KIND_RESUMED: A resumed suggestion (comes from a span, currently this type is used only
+ // in java for re-correction)
+ static const int KIND_RESUMED = 9;
+ static const int KIND_OOV_CORRECTION = 10; // Most probable string correction
static const int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java
index 9014e7cc8..eea1efc49 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java
@@ -17,11 +17,13 @@
package com.android.inputmethod.keyboard.internal;
import android.app.Instrumentation;
+import android.content.Context;
+import android.content.res.Resources;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.inputmethod.latin.CollectionUtils;
-import com.android.inputmethod.latin.StringUtils;
+import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -29,16 +31,24 @@ import java.util.Arrays;
import java.util.Locale;
@MediumTest
-public class KeySpecParserCsvTests extends InstrumentationTestCase {
- private final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
+public class KeySpecParserSplitTests extends InstrumentationTestCase {
+ private static final Locale TEST_LOCALE = Locale.ENGLISH;
+ final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@Override
protected void setUp() throws Exception {
super.setUp();
final Instrumentation instrumentation = getInstrumentation();
- mTextsSet.setLanguage(Locale.ENGLISH.getLanguage());
- mTextsSet.loadStringResources(instrumentation.getTargetContext());
+ final Context targetContext = instrumentation.getTargetContext();
+ mTextsSet.setLanguage(TEST_LOCALE.getLanguage());
+ new RunInLocale<Void>() {
+ @Override
+ protected Void job(final Resources res) {
+ mTextsSet.loadStringResources(targetContext);
+ return null;
+ }
+ }.runInLocale(targetContext.getResources(), TEST_LOCALE);
final String[] testResourceNames = getAllResourceIdNames(
com.android.inputmethod.latin.tests.R.string.class);
mTextsSet.loadStringResourcesInternal(instrumentation.getContext(),
@@ -56,8 +66,8 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
return names.toArray(new String[names.size()]);
}
- private static void assertArrayEquals(final String message, final Object[] expected,
- final Object[] actual) {
+ private static <T> void assertArrayEquals(final String message, final T[] expected,
+ final T[] actual) {
if (expected == actual) {
return;
}
@@ -70,15 +80,19 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
return;
}
for (int i = 0; i < expected.length; i++) {
- assertEquals(message + " [" + i + "]",
- Arrays.toString(expected), Arrays.toString(actual));
+ final T e = expected[i];
+ final T a = actual[i];
+ if (e == a) {
+ continue;
+ }
+ assertEquals(message + " [" + i + "]", e, a);
}
}
private void assertTextArray(final String message, final String value,
final String ... expectedArray) {
final String resolvedActual = KeySpecParser.resolveTextReference(value, mTextsSet);
- final String[] actual = StringUtils.parseCsvString(resolvedActual);
+ final String[] actual = KeySpecParser.splitKeySpecs(resolvedActual);
final String[] expected = (expectedArray.length == 0) ? null : expectedArray;
assertArrayEquals(message, expected, actual);
}
@@ -101,7 +115,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
private static final String SURROGATE1 = PAIR1 + PAIR2;
private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3;
- public void testParseCsvTextZero() {
+ public void testSplitZero() {
assertTextArray("Empty string", "");
assertTextArray("Empty entry", ",");
assertTextArray("Empty entry at beginning", ",a", "a");
@@ -110,7 +124,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
assertTextArray("Empty entries with escape", ",a,b\\,c,,d,", "a", "b\\,c", "d");
}
- public void testParseCsvTextSingle() {
+ public void testSplitSingle() {
assertTextArray("Single char", "a", "a");
assertTextArray("Surrogate pair", PAIR1, PAIR1);
assertTextArray("Single escape", "\\", "\\");
@@ -139,7 +153,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
assertTextArray("Incomplete resource reference 4", "!" + SURROGATE2, "!" + SURROGATE2);
}
- public void testParseCsvTextSingleEscaped() {
+ public void testSplitSingleEscaped() {
assertTextArray("Escaped char", "\\a", "\\a");
assertTextArray("Escaped surrogate pair", "\\" + PAIR1, "\\" + PAIR1);
assertTextArray("Escaped comma", "\\,", "\\,");
@@ -174,7 +188,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
assertTextArray("Escaped !TEXT/NAME", "\\!TEXT/EMPTY_STRING", "\\!TEXT/EMPTY_STRING");
}
- public void testParseCsvTextMulti() {
+ public void testSplitMulti() {
assertTextArray("Multiple chars", "a,b,c", "a", "b", "c");
assertTextArray("Multiple chars", "a,b,\\c", "a", "b", "\\c");
assertTextArray("Multiple chars and escape at beginning and end",
@@ -189,7 +203,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
" abc ", " def ", " ghi ");
}
- public void testParseCsvTextMultiEscaped() {
+ public void testSplitMultiEscaped() {
assertTextArray("Multiple chars with comma", "a,\\,,c", "a", "\\,", "c");
assertTextArray("Multiple chars with comma surrounded by spaces", " a , \\, , c ",
" a ", " \\, ", " c ");
@@ -208,17 +222,17 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
"\\!", "\\!TEXT/EMPTY_STRING");
}
- public void testParseCsvResourceError() {
+ public void testSplitResourceError() {
assertError("Incomplete resource name", "!text/", "!text/");
assertError("Non existing resource", "!text/non_existing");
}
- public void testParseCsvResourceZero() {
+ public void testSplitResourceZero() {
assertTextArray("Empty string",
"!text/empty_string");
}
- public void testParseCsvResourceSingle() {
+ public void testSplitResourceSingle() {
assertTextArray("Single char",
"!text/single_char", "a");
assertTextArray("Space",
@@ -240,7 +254,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
"\\\\!text/single_char", "\\\\a");
}
- public void testParseCsvResourceSingleEscaped() {
+ public void testSplitResourceSingleEscaped() {
assertTextArray("Escaped char",
"!text/escaped_char", "\\a");
assertTextArray("Escaped comma",
@@ -267,7 +281,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
"!text/escaped_label_with_escape", "a\\\\c");
}
- public void testParseCsvResourceMulti() {
+ public void testSplitResourceMulti() {
assertTextArray("Multiple chars",
"!text/multiple_chars", "a", "b", "c");
assertTextArray("Multiple chars surrounded by spaces",
@@ -279,7 +293,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
"!text/multiple_labels_surrounded_by_spaces", " abc ", " def ", " ghi ");
}
- public void testParseCsvResourcetMultiEscaped() {
+ public void testSplitResourcetMultiEscaped() {
assertTextArray("Multiple chars with comma",
"!text/multiple_chars_with_comma",
"a", "\\,", "c");
@@ -300,7 +314,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
" ab\\\\ ", " d\\\\\\, ", " g\\,i ");
}
- public void testParseMultipleResources() {
+ public void testSplitMultipleResources() {
assertTextArray("Literals and resources",
"1,!text/multiple_chars,z", "1", "a", "b", "c", "z");
assertTextArray("Literals and resources and escape at end",
@@ -322,7 +336,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
"abcabc", "def", "ghi");
}
- public void testParseIndirectReference() {
+ public void testSplitIndirectReference() {
assertTextArray("Indirect",
"!text/indirect_string", "a", "b", "c");
assertTextArray("Indirect with literal",
@@ -331,7 +345,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase {
"!text/indirect2_string", "a", "b", "c");
}
- public void testParseInfiniteIndirectReference() {
+ public void testSplitInfiniteIndirectReference() {
assertError("Infinite indirection",
"1,!text/infinite_indirection,2", "1", "infinite", "<infinite>", "loop", "2");
}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
index 861abe831..b55158d3e 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
@@ -20,18 +20,22 @@ import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UN
import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT;
import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
+import android.content.Context;
+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.LocaleUtils.RunInLocale;
import java.util.Arrays;
import java.util.Locale;
@SmallTest
public class KeySpecParserTests extends AndroidTestCase {
- private final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
- private final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
+ private final static Locale TEST_LOCALE = Locale.ENGLISH;
+ final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
+ final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
private static final String CODE_SETTINGS = "!code/key_settings";
private static final String ICON_SETTINGS = "!icon/settings_key";
@@ -48,10 +52,17 @@ public class KeySpecParserTests extends AndroidTestCase {
protected void setUp() throws Exception {
super.setUp();
- final String language = Locale.ENGLISH.getLanguage();
+ final String language = TEST_LOCALE.getLanguage();
mCodesSet.setLanguage(language);
mTextsSet.setLanguage(language);
- mTextsSet.loadStringResources(getContext());
+ final Context context = getContext();
+ new RunInLocale<Void>() {
+ @Override
+ protected Void job(final Resources res) {
+ mTextsSet.loadStringResources(context);
+ return null;
+ }
+ }.runInLocale(context.getResources(), TEST_LOCALE);
mCodeSettings = KeySpecParser.parseCode(
CODE_SETTINGS, mCodesSet, CODE_UNSPECIFIED);
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MatrixUtilsTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MatrixUtilsTests.java
new file mode 100644
index 000000000..e2a11ab99
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MatrixUtilsTests.java
@@ -0,0 +1,84 @@
+/*
+ * 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 com.android.inputmethod.keyboard.internal.MatrixUtils.MatrixOperationFailedException;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class MatrixUtilsTests extends AndroidTestCase {
+ // "run tests" -c com.android.inputmethod.keyboard.internal.MatrixUtilsTests
+ private static final boolean DEBUG = false;
+ private static final float EPSILON = 0.00001f;
+
+ private static void assertEqualsFloat(float f0, float f1) {
+ assertEqualsFloat(f0, f1, EPSILON);
+ }
+
+ /* package */ static void assertEqualsFloat(float f0, float f1, float error) {
+ assertTrue(Math.abs(f0 - f1) < error);
+ }
+
+ public void testMulti() {
+ final float[][] matrixA = {{1, 2}, {3, 4}};
+ final float[][] matrixB = {{5, 6}, {7, 8}};
+ final float[][] retval = new float[2][2];
+ try {
+ MatrixUtils.multiply(matrixA, matrixB, retval);
+ } catch (MatrixOperationFailedException e) {
+ assertTrue(false);
+ }
+ if (DEBUG) {
+ MatrixUtils.dump("multi", retval);
+ }
+ assertEqualsFloat(retval[0][0], 19);
+ assertEqualsFloat(retval[0][1], 22);
+ assertEqualsFloat(retval[1][0], 43);
+ assertEqualsFloat(retval[1][1], 50);
+ }
+
+ public void testInverse() {
+ final int N = 4;
+ final float[][] matrix =
+ {{1, 2, 3, 4}, {4, 0, 5, 6}, {6, 4, 2, 0}, {6, 4, 2, 1}};
+ final float[][] inverse = new float[N][N];
+ final float[][] tempMatrix = new float[N][N];
+ for (int i = 0; i < N; ++i) {
+ for (int j = 0; j < N; ++j) {
+ tempMatrix[i][j] = matrix[i][j];
+ }
+ }
+ final float[][] retval = new float[N][N];
+ try {
+ MatrixUtils.inverse(tempMatrix, inverse);
+ } catch (MatrixOperationFailedException e) {
+ assertTrue(false);
+ }
+ try {
+ MatrixUtils.multiply(matrix, inverse, retval);
+ } catch (MatrixOperationFailedException e) {
+ assertTrue(false);
+ }
+ for (int i = 0; i < N; ++i) {
+ for (int j = 0; j < N; ++j) {
+ assertEqualsFloat(((i == j) ? 1.0f : 0.0f), retval[i][j]);
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/SmoothingUtilsTests.java b/tests/src/com/android/inputmethod/keyboard/internal/SmoothingUtilsTests.java
new file mode 100644
index 000000000..293741af2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/SmoothingUtilsTests.java
@@ -0,0 +1,45 @@
+/*
+ * 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 com.android.inputmethod.keyboard.internal.MatrixUtils.MatrixOperationFailedException;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class SmoothingUtilsTests extends AndroidTestCase {
+ // "run tests" -c com.android.inputmethod.keyboard.internal.SmoothingUtilsTests
+ private static final boolean DEBUG = false;
+
+ public void testGet3DParamaters() {
+ final float[] xs = new float[] {0, 1, 2, 3, 4};
+ final float[] ys = new float[] {1, 4, 15, 40, 85}; // y = x^3 + x^2 + x + 1
+ final float[][] retval = new float[4][1];
+ try {
+ SmoothingUtils.get3DParameters(xs, ys, retval);
+ if (DEBUG) {
+ MatrixUtils.dump("3d", retval);
+ }
+ for (int i = 0; i < 4; ++i) {
+ MatrixUtilsTests.assertEqualsFloat(retval[i][0], 1.0f, 0.001f);
+ }
+ } catch (MatrixOperationFailedException e) {
+ assertTrue(false);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
index e1149b3fd..301582979 100644
--- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
@@ -108,13 +108,13 @@ public class InputPointersTests extends AndroidTestCase {
assertNotSame("pointerIds after copy", dst.getPointerIds(), src.getPointerIds());
assertNotSame("times after copy", dst.getTimes(), src.getTimes());
final int size = dst.getPointerSize();
- assertArrayEquals("xCoordinates values after copy",
+ assertIntArrayEquals("xCoordinates values after copy",
dst.getXCoordinates(), 0, src.getXCoordinates(), 0, size);
- assertArrayEquals("yCoordinates values after copy",
+ assertIntArrayEquals("yCoordinates values after copy",
dst.getYCoordinates(), 0, src.getYCoordinates(), 0, size);
- assertArrayEquals("pointerIds values after copy",
+ assertIntArrayEquals("pointerIds values after copy",
dst.getPointerIds(), 0, src.getPointerIds(), 0, size);
- assertArrayEquals("times values after copy",
+ assertIntArrayEquals("times values after copy",
dst.getTimes(), 0, src.getTimes(), 0, size);
}
@@ -135,34 +135,34 @@ public class InputPointersTests extends AndroidTestCase {
dst.append(src, 0, 0);
assertEquals("size after append zero", dstLen, dst.getPointerSize());
- assertArrayEquals("xCoordinates after append zero",
+ assertIntArrayEquals("xCoordinates after append zero",
dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
- assertArrayEquals("yCoordinates after append zero",
+ assertIntArrayEquals("yCoordinates after append zero",
dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
- assertArrayEquals("pointerIds after append zero",
+ assertIntArrayEquals("pointerIds after append zero",
dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
- assertArrayEquals("times after append zero",
+ assertIntArrayEquals("times after append zero",
dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
dst.append(src, 0, srcLen);
assertEquals("size after append", dstLen + srcLen, dst.getPointerSize());
assertTrue("primitive length after append",
dst.getPointerIds().length >= dstLen + srcLen);
- assertArrayEquals("original xCoordinates values after append",
+ assertIntArrayEquals("original xCoordinates values after append",
dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
- assertArrayEquals("original yCoordinates values after append",
+ assertIntArrayEquals("original yCoordinates values after append",
dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
- assertArrayEquals("original pointerIds values after append",
+ assertIntArrayEquals("original pointerIds values after append",
dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
- assertArrayEquals("original times values after append",
+ assertIntArrayEquals("original times values after append",
dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
- assertArrayEquals("appended xCoordinates values after append",
+ assertIntArrayEquals("appended xCoordinates values after append",
src.getXCoordinates(), 0, dst.getXCoordinates(), dstLen, srcLen);
- assertArrayEquals("appended yCoordinates values after append",
+ assertIntArrayEquals("appended yCoordinates values after append",
src.getYCoordinates(), 0, dst.getYCoordinates(), dstLen, srcLen);
- assertArrayEquals("appended pointerIds values after append",
+ assertIntArrayEquals("appended pointerIds values after append",
src.getPointerIds(), 0, dst.getPointerIds(), dstLen, srcLen);
- assertArrayEquals("appended times values after append",
+ assertIntArrayEquals("appended times values after append",
src.getTimes(), 0, dst.getTimes(), dstLen, srcLen);
}
@@ -190,47 +190,55 @@ public class InputPointersTests extends AndroidTestCase {
dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords, 0, 0);
assertEquals("size after append zero", dstLen, dst.getPointerSize());
- assertArrayEquals("xCoordinates after append zero",
+ assertIntArrayEquals("xCoordinates after append zero",
dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
- assertArrayEquals("yCoordinates after append zero",
+ assertIntArrayEquals("yCoordinates after append zero",
dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
- assertArrayEquals("pointerIds after append zero",
+ assertIntArrayEquals("pointerIds after append zero",
dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
- assertArrayEquals("times after append zero",
+ assertIntArrayEquals("times after append zero",
dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords, 0, srcLen);
assertEquals("size after append", dstLen + srcLen, dst.getPointerSize());
assertTrue("primitive length after append",
dst.getPointerIds().length >= dstLen + srcLen);
- assertArrayEquals("original xCoordinates values after append",
+ assertIntArrayEquals("original xCoordinates values after append",
dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen);
- assertArrayEquals("original yCoordinates values after append",
+ assertIntArrayEquals("original yCoordinates values after append",
dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen);
- assertArrayEquals("original pointerIds values after append",
+ assertIntArrayEquals("original pointerIds values after append",
dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen);
- assertArrayEquals("original times values after append",
+ assertIntArrayEquals("original times values after append",
dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen);
- assertArrayEquals("appended xCoordinates values after append",
+ assertIntArrayEquals("appended xCoordinates values after append",
srcXCoords.getPrimitiveArray(), 0, dst.getXCoordinates(), dstLen, srcLen);
- assertArrayEquals("appended yCoordinates values after append",
+ assertIntArrayEquals("appended yCoordinates values after append",
srcYCoords.getPrimitiveArray(), 0, dst.getYCoordinates(), dstLen, srcLen);
- assertArrayEquals("appended pointerIds values after append",
+ assertIntArrayEquals("appended pointerIds values after append",
srcPointerIds, 0, dst.getPointerIds(), dstLen, srcLen);
- assertArrayEquals("appended times values after append",
+ assertIntArrayEquals("appended times values after append",
srcTimes.getPrimitiveArray(), 0, dst.getTimes(), dstLen, srcLen);
}
- private static void assertArrayEquals(String message, int[] expecteds, int expectedPos,
- int[] actuals, int actualPos, int length) {
- if (expecteds == null && actuals == null) {
+ // TODO: Consolidate this method with
+ // {@link ResizableIntArrayTests#assertIntArrayEquals(String,int[],int,int[],int,int)}.
+ private static void assertIntArrayEquals(final String message, final int[] expecteds,
+ final int expectedPos, final int[] actuals, final int actualPos, final int length) {
+ if (expecteds == actuals) {
return;
}
if (expecteds == null || actuals == null) {
- fail(message + ": expecteds=" + expecteds + " actuals=" + actuals);
+ assertEquals(message, Arrays.toString(expecteds), Arrays.toString(actuals));
+ return;
+ }
+ if (expecteds.length < expectedPos + length || actuals.length < actualPos + length) {
+ fail(message + ": insufficient length: expecteds=" + Arrays.toString(expecteds)
+ + " actuals=" + Arrays.toString(actuals));
+ return;
}
for (int i = 0; i < length; i++) {
- assertEquals(message + ": element at " + i,
+ assertEquals(message + " [" + i + "]",
expecteds[i + expectedPos], actuals[i + actualPos]);
}
}
diff --git a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java
index 2d1b836c2..b9fee950e 100644
--- a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java
+++ b/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java
@@ -19,6 +19,8 @@ package com.android.inputmethod.latin;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.Arrays;
+
@SmallTest
public class ResizableIntArrayTests extends AndroidTestCase {
private static final int DEFAULT_CAPACITY = 48;
@@ -186,7 +188,7 @@ public class ResizableIntArrayTests extends AndroidTestCase {
assertEquals("length after copy", dst.getLength(), src.getLength());
assertSame("array after copy", array, dst.getPrimitiveArray());
assertNotSame("array after copy", dst.getPrimitiveArray(), src.getPrimitiveArray());
- assertArrayEquals("values after copy",
+ assertIntArrayEquals("values after copy",
dst.getPrimitiveArray(), 0, src.getPrimitiveArray(), 0, dst.getLength());
final int smallerLength = DEFAULT_CAPACITY / 2;
@@ -197,7 +199,7 @@ public class ResizableIntArrayTests extends AndroidTestCase {
assertEquals("length after copy to smaller", dst.getLength(), src.getLength());
assertNotSame("array after copy to smaller", array2, array3);
assertNotSame("array after copy to smaller", array3, src.getPrimitiveArray());
- assertArrayEquals("values after copy to smaller",
+ assertIntArrayEquals("values after copy to smaller",
dst.getPrimitiveArray(), 0, src.getPrimitiveArray(), 0, dst.getLength());
}
@@ -220,7 +222,7 @@ public class ResizableIntArrayTests extends AndroidTestCase {
dst.append(src, 0, 0);
assertEquals("length after append zero", dstLen, dst.getLength());
assertSame("array after append zero", array, dst.getPrimitiveArray());
- assertArrayEquals("values after append zero",
+ assertIntArrayEquals("values after append zero",
dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen);
dst.append(src, 0, srcLen);
@@ -228,9 +230,9 @@ public class ResizableIntArrayTests extends AndroidTestCase {
assertSame("array after append", array, dst.getPrimitiveArray());
assertTrue("primitive length after append",
dst.getPrimitiveArray().length >= dstLen + srcLen);
- assertArrayEquals("original values after append",
+ assertIntArrayEquals("original values after append",
dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen);
- assertArrayEquals("appended values after append",
+ assertIntArrayEquals("appended values after append",
src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen, srcLen);
dst.append(src, 0, srcLen);
@@ -238,11 +240,11 @@ public class ResizableIntArrayTests extends AndroidTestCase {
assertNotSame("array after 2nd append", array, dst.getPrimitiveArray());
assertTrue("primitive length after 2nd append",
dst.getPrimitiveArray().length >= dstLen + srcLen * 2);
- assertArrayEquals("original values after 2nd append",
+ assertIntArrayEquals("original values after 2nd append",
dstCopy.getPrimitiveArray(), 0, dst.getPrimitiveArray(), 0, dstLen);
- assertArrayEquals("appended values after 2nd append",
+ assertIntArrayEquals("appended values after 2nd append",
src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen, srcLen);
- assertArrayEquals("appended values after 2nd append",
+ assertIntArrayEquals("appended values after 2nd append",
src.getPrimitiveArray(), 0, dst.getPrimitiveArray(), dstLen + srcLen, srcLen);
}
@@ -319,16 +321,22 @@ public class ResizableIntArrayTests extends AndroidTestCase {
}
}
- private static void assertArrayEquals(String message, int[] expecteds, int expectedPos,
- int[] actuals, int actualPos, int length) {
- if (expecteds == null && actuals == null) {
+ private static void assertIntArrayEquals(final String message, final int[] expecteds,
+ final int expectedPos, final int[] actuals, final int actualPos, final int length) {
+ if (expecteds == actuals) {
return;
}
if (expecteds == null || actuals == null) {
- fail(message + ": expecteds=" + expecteds + " actuals=" + actuals);
+ assertEquals(message, Arrays.toString(expecteds), Arrays.toString(actuals));
+ return;
+ }
+ if (expecteds.length < expectedPos + length || actuals.length < actualPos + length) {
+ fail(message + ": insufficient length: expecteds=" + Arrays.toString(expecteds)
+ + " actuals=" + Arrays.toString(actuals));
+ return;
}
for (int i = 0; i < length; i++) {
- assertEquals(message + ": element at " + i,
+ assertEquals(message + " [" + i + "]",
expecteds[i + expectedPos], actuals[i + actualPos]);
}
}
diff --git a/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java b/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java
index ed16846b9..c915522ee 100644
--- a/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java
@@ -19,25 +19,41 @@ package com.android.inputmethod.latin;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.inputmethod.latin.ResourceUtils.DeviceOverridePatternSyntaxError;
+
import java.util.HashMap;
@SmallTest
public class ResourceUtilsTests extends AndroidTestCase {
public void testFindDefaultConstant() {
final String[] nullArray = null;
- assertNull(ResourceUtils.findDefaultConstant(nullArray));
-
final String[] emptyArray = {};
- assertNull(ResourceUtils.findDefaultConstant(emptyArray));
-
final String[] array = {
+ "HARDWARE=grouper,0.3",
+ "HARDWARE=mako,0.4",
+ ",defaultValue1",
+ "HARDWARE=manta,0.2",
+ ",defaultValue2",
+ };
+
+ try {
+ assertNull(ResourceUtils.findDefaultConstant(nullArray));
+ assertNull(ResourceUtils.findDefaultConstant(emptyArray));
+ assertEquals(ResourceUtils.findDefaultConstant(array), "defaultValue1");
+ } catch (final DeviceOverridePatternSyntaxError e) {
+ fail(e.getMessage());
+ }
+
+ final String[] errorArray = {
"HARDWARE=grouper,0.3",
- "HARDWARE=mako,0.4",
- ",defaultValue1",
- "HARDWARE=manta,0.2",
- ",defaultValue2",
+ "no_comma"
};
- assertEquals(ResourceUtils.findDefaultConstant(array), "defaultValue1");
+ try {
+ final String defaultValue = ResourceUtils.findDefaultConstant(errorArray);
+ fail("exception should be thrown: defaultValue=" + defaultValue);
+ } catch (final DeviceOverridePatternSyntaxError e) {
+ assertEquals("Array element has no comma: no_comma", e.getMessage());
+ }
}
public void testFindConstantForKeyValuePairsSimple() {
@@ -67,33 +83,23 @@ public class ResourceUtilsTests extends AndroidTestCase {
final HashMap<String,String> keyValues = CollectionUtils.newHashMap();
keyValues.put(HARDWARE_KEY, "grouper");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+ assertEquals("0.3", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.put(HARDWARE_KEY, "mako");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+ assertEquals("0.4", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.put(HARDWARE_KEY, "manta");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+ assertEquals("0.2", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
+
+ keyValues.clear();
+ keyValues.put("hardware", "grouper");
+ assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
- try {
- keyValues.clear();
- keyValues.put("hardware", "grouper");
- final String constant = ResourceUtils.findConstantForKeyValuePairs(keyValues, array);
- fail("condition without HARDWARE must fail: constant=" + constant);
- } catch (final RuntimeException e) {
- assertEquals(e.getMessage(), "Found unknown key: HARDWARE=grouper");
- }
keyValues.clear();
keyValues.put(HARDWARE_KEY, "MAKO");
assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.put(HARDWARE_KEY, "mantaray");
assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
- try {
- final String constant = ResourceUtils.findConstantForKeyValuePairs(
- emptyKeyValue, array);
- fail("emptyCondition shouldn't match: constant=" + constant);
- } catch (final RuntimeException e) {
- assertEquals(e.getMessage(), "Found unknown key: HARDWARE=grouper");
- }
+ assertNull(ResourceUtils.findConstantForKeyValuePairs(emptyKeyValue, array));
}
public void testFindConstantForKeyValuePairsCombined() {
@@ -102,6 +108,8 @@ public class ResourceUtilsTests extends AndroidTestCase {
final String MANUFACTURER_KEY = "MANUFACTURER";
final String[] array = {
",defaultValue",
+ "no_comma",
+ "error_pattern,0.1",
"HARDWARE=grouper:MANUFACTURER=asus,0.3",
"HARDWARE=mako:MODEL=Nexus 4,0.4",
"HARDWARE=manta:MODEL=Nexus 10:MANUFACTURER=samsung,0.2"
@@ -117,25 +125,25 @@ public class ResourceUtilsTests extends AndroidTestCase {
keyValues.put(HARDWARE_KEY, "grouper");
keyValues.put(MODEL_KEY, "Nexus 7");
keyValues.put(MANUFACTURER_KEY, "asus");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+ assertEquals("0.3", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray));
keyValues.clear();
keyValues.put(HARDWARE_KEY, "mako");
keyValues.put(MODEL_KEY, "Nexus 4");
keyValues.put(MANUFACTURER_KEY, "LGE");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+ assertEquals("0.4", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray));
keyValues.clear();
keyValues.put(HARDWARE_KEY, "manta");
keyValues.put(MODEL_KEY, "Nexus 10");
keyValues.put(MANUFACTURER_KEY, "samsung");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+ assertEquals("0.2", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray));
keyValues.put(HARDWARE_KEY, "mantaray");
assertNull(ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray), "0.2");
+ assertEquals("0.2", ResourceUtils.findConstantForKeyValuePairs(keyValues, failArray));
}
public void testFindConstantForKeyValuePairsRegexp() {
@@ -144,6 +152,8 @@ public class ResourceUtilsTests extends AndroidTestCase {
final String MANUFACTURER_KEY = "MANUFACTURER";
final String[] array = {
",defaultValue",
+ "no_comma",
+ "HARDWARE=error_regexp:MANUFACTURER=error[regexp,0.1",
"HARDWARE=grouper|tilapia:MANUFACTURER=asus,0.3",
"HARDWARE=[mM][aA][kK][oO]:MODEL=Nexus 4,0.4",
"HARDWARE=manta.*:MODEL=Nexus 10:MANUFACTURER=samsung,0.2"
@@ -153,24 +163,24 @@ public class ResourceUtilsTests extends AndroidTestCase {
keyValues.put(HARDWARE_KEY, "grouper");
keyValues.put(MODEL_KEY, "Nexus 7");
keyValues.put(MANUFACTURER_KEY, "asus");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+ assertEquals("0.3", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.put(HARDWARE_KEY, "tilapia");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.3");
+ assertEquals("0.3", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.clear();
keyValues.put(HARDWARE_KEY, "mako");
keyValues.put(MODEL_KEY, "Nexus 4");
keyValues.put(MANUFACTURER_KEY, "LGE");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+ assertEquals("0.4", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.put(HARDWARE_KEY, "MAKO");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.4");
+ assertEquals("0.4", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.clear();
keyValues.put(HARDWARE_KEY, "manta");
keyValues.put(MODEL_KEY, "Nexus 10");
keyValues.put(MANUFACTURER_KEY, "samsung");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+ assertEquals("0.2", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
keyValues.put(HARDWARE_KEY, "mantaray");
- assertEquals(ResourceUtils.findConstantForKeyValuePairs(keyValues, array), "0.2");
+ assertEquals("0.2", ResourceUtils.findConstantForKeyValuePairs(keyValues, array));
}
}
diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
index 29e790a7d..4f260987b 100644
--- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
@@ -40,57 +40,62 @@ public class StringUtilsTests extends AndroidTestCase {
}));
}
- public void testContainsInCsv() {
- assertFalse("null", StringUtils.containsInCsv("key", null));
- assertFalse("empty", StringUtils.containsInCsv("key", ""));
- assertFalse("not in 1 element", StringUtils.containsInCsv("key", "key1"));
- assertFalse("not in 2 elements", StringUtils.containsInCsv("key", "key1,key2"));
-
- assertTrue("in 1 element", StringUtils.containsInCsv("key", "key"));
- assertTrue("in 2 elements", StringUtils.containsInCsv("key", "key1,key"));
+ public void testContainsInExtraValues() {
+ assertFalse("null", StringUtils.containsInCommaSplittableText("key", null));
+ assertFalse("empty", StringUtils.containsInCommaSplittableText("key", ""));
+ assertFalse("not in 1 element",
+ StringUtils.containsInCommaSplittableText("key", "key1"));
+ assertFalse("not in 2 elements",
+ StringUtils.containsInCommaSplittableText("key", "key1,key2"));
+
+ assertTrue("in 1 element", StringUtils.containsInCommaSplittableText("key", "key"));
+ assertTrue("in 2 elements", StringUtils.containsInCommaSplittableText("key", "key1,key"));
}
- public void testAppendToCsvIfNotExists() {
- assertEquals("null", "key", StringUtils.appendToCsvIfNotExists("key", null));
- assertEquals("empty", "key", StringUtils.appendToCsvIfNotExists("key", ""));
+ public void testAppendToExtraValuesIfNotExists() {
+ assertEquals("null", "key",
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", null));
+ assertEquals("empty", "key",
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", ""));
assertEquals("not in 1 element", "key1,key",
- StringUtils.appendToCsvIfNotExists("key", "key1"));
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1"));
assertEquals("not in 2 elements", "key1,key2,key",
- StringUtils.appendToCsvIfNotExists("key", "key1,key2"));
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key2"));
assertEquals("in 1 element", "key",
- StringUtils.appendToCsvIfNotExists("key", "key"));
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", "key"));
assertEquals("in 2 elements at position 1", "key,key2",
- StringUtils.appendToCsvIfNotExists("key", "key,key2"));
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", "key,key2"));
assertEquals("in 2 elements at position 2", "key1,key",
- StringUtils.appendToCsvIfNotExists("key", "key1,key"));
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key"));
assertEquals("in 3 elements at position 2", "key1,key,key3",
- StringUtils.appendToCsvIfNotExists("key", "key1,key,key3"));
+ StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key,key3"));
}
- public void testRemoveFromCsvIfExists() {
- assertEquals("null", "", StringUtils.removeFromCsvIfExists("key", null));
- assertEquals("empty", "", StringUtils.removeFromCsvIfExists("key", ""));
+ public void testRemoveFromExtraValuesIfExists() {
+ assertEquals("null", "", StringUtils.removeFromCommaSplittableTextIfExists("key", null));
+ assertEquals("empty", "", StringUtils.removeFromCommaSplittableTextIfExists("key", ""));
assertEquals("not in 1 element", "key1",
- StringUtils.removeFromCsvIfExists("key", "key1"));
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1"));
assertEquals("not in 2 elements", "key1,key2",
- StringUtils.removeFromCsvIfExists("key", "key1,key2"));
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key2"));
assertEquals("in 1 element", "",
- StringUtils.removeFromCsvIfExists("key", "key"));
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key"));
assertEquals("in 2 elements at position 1", "key2",
- StringUtils.removeFromCsvIfExists("key", "key,key2"));
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key,key2"));
assertEquals("in 2 elements at position 2", "key1",
- StringUtils.removeFromCsvIfExists("key", "key1,key"));
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key"));
assertEquals("in 3 elements at position 2", "key1,key3",
- StringUtils.removeFromCsvIfExists("key", "key1,key,key3"));
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key1,key,key3"));
assertEquals("in 3 elements at position 1,2,3", "",
- StringUtils.removeFromCsvIfExists("key", "key,key,key"));
+ StringUtils.removeFromCommaSplittableTextIfExists("key", "key,key,key"));
assertEquals("in 5 elements at position 2,4", "key1,key3,key5",
- StringUtils.removeFromCsvIfExists("key", "key1,key,key3,key,key5"));
+ StringUtils.removeFromCommaSplittableTextIfExists(
+ "key", "key1,key,key3,key,key5"));
}