aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/xml/method.xml6
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java13
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java43
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java33
-rw-r--r--native/jni/src/suggest/core/layout/proximity_info_params.cpp5
-rw-r--r--native/jni/src/suggest/core/layout/proximity_info_params.h3
-rw-r--r--native/jni/src/suggest/core/layout/proximity_info_state.cpp8
-rw-r--r--native/jni/src/suggest/core/layout/proximity_info_state.h10
-rw-r--r--native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp63
-rw-r--r--native/jni/src/suggest/core/layout/proximity_info_state_utils.h4
-rw-r--r--native/jni/src/suggest/core/policy/traversal.h1
-rw-r--r--native/jni/src/suggest/core/suggest.cpp2
-rw-r--r--native/jni/src/suggest/policyimpl/typing/typing_traversal.h4
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java4
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java36
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java64
16 files changed, 210 insertions, 89 deletions
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 1bef3c254..6d8f78773 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -40,7 +40,7 @@
eo: Esperanto/spanish
es: Spanish/spanish
es_US: Spanish (United States)/spanish
- (es_419: Spanish (Latin America)/qwerty)
+ es_419: Spanish (Latin America)/spanish
et_EE: Estonian (Estonia)/nordic
eu_ES: Basque (Spain)/spanish
fa: Persian/farsi
@@ -248,16 +248,14 @@
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
- <!--
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
- android:subtypeId="0x623f9286"
+ android:subtypeId="0xa23e5d19"
android:imeSubtypeLocale="es_419"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
- -->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xec2d3955"
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 7b37777f5..5e36d9703 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -247,7 +247,9 @@ public final class BinaryDictionary extends Dictionary {
final String prevWord, final ProximityInfo proximityInfo,
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId, final float[] inOutLanguageWeight) {
- if (!isValidDictionary()) return null;
+ if (!isValidDictionary()) {
+ return null;
+ }
Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
// TODO: toLowerCase in the native code
@@ -257,12 +259,11 @@ public final class BinaryDictionary extends Dictionary {
final boolean isGesture = composer.isBatchMode();
final int inputSize;
if (!isGesture) {
- final int composerSize = composer.sizeWithoutTrailingSingleQuotes();
- if (composerSize > MAX_WORD_LENGTH - 1) return null;
- for (int i = 0; i < composerSize; i++) {
- mInputCodePoints[i] = composer.getCodeAt(i);
+ inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
+ mInputCodePoints, MAX_WORD_LENGTH);
+ if (inputSize < 0) {
+ return null;
}
- inputSize = composerSize;
} else {
inputSize = inputPointers.getPointerSize();
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 81d642ff2..02f18cdd3 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -131,29 +131,42 @@ public final class WordComposer {
return mCodePointSize;
}
- public boolean isSingleLetter() {
- return size() == 1;
+ /**
+ * Copy the code points in the typed word to a destination array of ints.
+ *
+ * If the array is too small to hold the code points in the typed word, nothing is copied and
+ * -1 is returned.
+ *
+ * @param destination the array of ints.
+ * @param maxSize the size of the array.
+ * @return the number of copied code points.
+ */
+ public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
+ final int[] destination, final int maxSize) {
+ int i = mTypedWordCache.length() - 1;
+ while (i >= 0 && mTypedWordCache.charAt(i) == Constants.CODE_SINGLE_QUOTE) {
+ --i;
+ }
+ if (i < 0) {
+ // The string is empty or contains only single quotes.
+ return 0;
+ }
+ final int codePointSize = Character.codePointCount(mTypedWordCache, 0, i);
+ if (codePointSize > maxSize) {
+ return -1;
+ }
+ return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0,
+ i + 1, true /* downCase */);
}
- // When the composition contains trailing quotes, we don't pass them to the suggestion engine.
- // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider
- // single quotes as separators because of their very common use as apostrophes.
- public int sizeWithoutTrailingSingleQuotes() {
- return size() - mTrailingSingleQuotesCount;
+ public boolean isSingleLetter() {
+ return size() == 1;
}
public final boolean isComposingWord() {
return size() > 0;
}
- // TODO: make sure that the index should not exceed MAX_WORD_LENGTH
- public int getCodeAt(int index) {
- if (index >= MAX_WORD_LENGTH) {
- return -1;
- }
- return mPrimaryKeyCodes[index];
- }
-
public InputPointers getInputPointers() {
return mInputPointers;
}
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index accbc8b7b..374badc19 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -191,13 +191,42 @@ public final class StringUtils {
}
final int[] codePoints =
new int[Character.codePointCount(charSequence, startIndex, endIndex)];
+ copyCodePointsAndReturnCodePointCount(codePoints, charSequence, startIndex, endIndex,
+ false /* downCase */);
+ return codePoints;
+ }
+
+ /**
+ * Copies the codepoints in a CharSequence to an int array.
+ *
+ * This method assumes there is enough space in the array to store the code points. The size
+ * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this
+ * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown.
+ * Also, this method makes no effort to be thread-safe. Do not modify the CharSequence while
+ * this method is running, or the behavior is undefined.
+ * This method can optionally downcase code points before copying them, but it pays no attention
+ * to locale while doing so.
+ *
+ * @param destination the int array.
+ * @param charSequence the CharSequence.
+ * @param startIndex the start index inside the string in java chars, inclusive.
+ * @param endIndex the end index inside the string in java chars, exclusive.
+ * @param downCase if this is true, code points will be downcased before being copied.
+ * @return the number of copied code points.
+ */
+ public static int copyCodePointsAndReturnCodePointCount(final int[] destination,
+ final CharSequence charSequence, final int startIndex, final int endIndex,
+ final boolean downCase) {
int destIndex = 0;
for (int index = startIndex; index < endIndex;
index = Character.offsetByCodePoints(charSequence, index, 1)) {
- codePoints[destIndex] = Character.codePointAt(charSequence, index);
+ final int codePoint = Character.codePointAt(charSequence, index);
+ // TODO: stop using this, as it's not aware of the locale and does not always do
+ // the right thing.
+ destination[destIndex] = downCase ? Character.toLowerCase(codePoint) : codePoint;
destIndex++;
}
- return codePoints;
+ return destIndex;
}
public static int[] toSortedCodePointArray(final String string) {
diff --git a/native/jni/src/suggest/core/layout/proximity_info_params.cpp b/native/jni/src/suggest/core/layout/proximity_info_params.cpp
index a70dd7e34..68bb0ae9d 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_params.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_params.cpp
@@ -24,9 +24,6 @@ const float ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE = 1.0f;
const float ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G = 0.5f;
/* Per method constants */
-// Used by ProximityInfoStateUtils::initGeometricDistanceInfos()
-const float ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
-
// Used by ProximityInfoStateUtils::updateNearKeysDistances()
const float ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_DISTANCE = 2.0f;
@@ -50,7 +47,7 @@ const int ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION = 2;
const int ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE = 4;
// Used by ProximityInfoStateUtils::updateAlignPointProbabilities()
-const float ProximityInfoParams::MIN_PROBABILITY = 0.000001f;
+const float ProximityInfoParams::MIN_PROBABILITY = 0.000005f;
const float ProximityInfoParams::MAX_SKIP_PROBABILITY = 0.95f;
const float ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY = 0.01f;
const float ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY = 0.1f;
diff --git a/native/jni/src/suggest/core/layout/proximity_info_params.h b/native/jni/src/suggest/core/layout/proximity_info_params.h
index b8e9f5daf..d9515c837 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_params.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_params.h
@@ -28,9 +28,6 @@ class ProximityInfoParams {
static const float VERTICAL_SWEET_SPOT_SCALE;
static const float VERTICAL_SWEET_SPOT_SCALE_G;
- // Used by ProximityInfoStateUtils::initGeometricDistanceInfos()
- static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD;
-
// Used by ProximityInfoStateUtils::updateNearKeysDistances()
static const float NEAR_KEY_THRESHOLD_FOR_DISTANCE;
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.cpp b/native/jni/src/suggest/core/layout/proximity_info_state.cpp
index b75c2ef67..e585f9088 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state.cpp
@@ -91,7 +91,6 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
mSampledInputIndice.clear();
mSampledLengthCache.clear();
mSampledNormalizedSquaredLengthCache.clear();
- mSampledNearKeySets.clear();
mSampledSearchKeySets.clear();
mSpeedRates.clear();
mBeelineSpeedPercentiles.clear();
@@ -126,18 +125,17 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
if (mSampledInputSize > 0) {
ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize,
lastSavedInputSize, isGeometric, &mSampledInputXs, &mSampledInputYs,
- &mSampledNearKeySets, &mSampledNormalizedSquaredLengthCache);
+ &mSampledNormalizedSquaredLengthCache);
if (isGeometric) {
// updates probabilities of skipping or mapping each key for all points.
ProximityInfoStateUtils::updateAlignPointProbabilities(
mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(),
mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize,
&mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache,
- &mSampledNormalizedSquaredLengthCache, &mSampledNearKeySets,
- mProximityInfo, &mCharProbabilities);
+ &mSampledNormalizedSquaredLengthCache, mProximityInfo, &mCharProbabilities);
ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo,
mSampledInputSize, lastSavedInputSize, &mSampledLengthCache,
- &mSampledNearKeySets, &mSampledSearchKeySets,
+ &mCharProbabilities, &mSampledSearchKeySets,
&mSampledSearchKeyVectors);
mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString(
mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString);
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.h b/native/jni/src/suggest/core/layout/proximity_info_state.h
index e253d9550..d66121b74 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_state.h
@@ -50,9 +50,9 @@ class ProximityInfoState {
mSampledInputXs(), mSampledInputYs(), mSampledTimes(), mSampledInputIndice(),
mSampledLengthCache(), mBeelineSpeedPercentiles(),
mSampledNormalizedSquaredLengthCache(), mSpeedRates(), mDirections(),
- mCharProbabilities(), mSampledNearKeySets(), mSampledSearchKeySets(),
- mSampledSearchKeyVectors(), mTouchPositionCorrectionEnabled(false),
- mSampledInputSize(0), mMostProbableStringProbability(0.0f) {
+ mCharProbabilities(), mSampledSearchKeySets(), mSampledSearchKeyVectors(),
+ mTouchPositionCorrectionEnabled(false), mSampledInputSize(0),
+ mMostProbableStringProbability(0.0f) {
memset(mInputProximities, 0, sizeof(mInputProximities));
memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord));
memset(mMostProbableString, 0, sizeof(mMostProbableString));
@@ -216,10 +216,6 @@ class ProximityInfoState {
std::vector<float> mDirections;
// probabilities of skipping or mapping to a key for each point.
std::vector<hash_map_compat<int, float> > mCharProbabilities;
- // The vector for the key code set which holds nearby keys for each sampled input point
- // 1. Used to calculate the probability of the key
- // 2. Used to calculate mSampledSearchKeySets
- std::vector<ProximityInfoStateUtils::NearKeycodesSet> mSampledNearKeySets;
// The vector for the key code set which holds nearby keys of some trailing sampled input points
// for each sampled input point. These nearby keys contain the next characters which can be in
// the dictionary. Specifically, currently we are looking for keys nearby trailing sampled
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
index 638297eb1..72bb68fc4 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
@@ -188,13 +188,10 @@ namespace latinime {
const int lastSavedInputSize, const bool isGeometric,
const std::vector<int> *const sampledInputXs,
const std::vector<int> *const sampledInputYs,
- std::vector<NearKeycodesSet> *sampledNearKeySets,
std::vector<float> *sampledNormalizedSquaredLengthCache) {
- sampledNearKeySets->resize(sampledInputSize);
const int keyCount = proximityInfo->getKeyCount();
sampledNormalizedSquaredLengthCache->resize(sampledInputSize * keyCount);
for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
- (*sampledNearKeySets)[i].reset();
for (int k = 0; k < keyCount; ++k) {
const int index = i * keyCount + k;
const int x = (*sampledInputXs)[i];
@@ -203,10 +200,6 @@ namespace latinime {
proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(
k, x, y, isGeometric);
(*sampledNormalizedSquaredLengthCache)[index] = normalizedSquaredDistance;
- if (normalizedSquaredDistance
- < ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) {
- (*sampledNearKeySets)[i][k] = true;
- }
}
}
}
@@ -626,7 +619,6 @@ namespace latinime {
const std::vector<float> *const sampledSpeedRates,
const std::vector<int> *const sampledLengthCache,
const std::vector<float> *const sampledNormalizedSquaredLengthCache,
- std::vector<NearKeycodesSet> *sampledNearKeySets,
const ProximityInfo *const proximityInfo,
std::vector<hash_map_compat<int, float> > *charProbabilities) {
charProbabilities->resize(sampledInputSize);
@@ -643,12 +635,10 @@ namespace latinime {
float nearestKeyDistance = static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
for (int j = 0; j < keyCount; ++j) {
- if ((*sampledNearKeySets)[i].test(j)) {
- const float distance = getPointToKeyByIdLength(
- maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j);
- if (distance < nearestKeyDistance) {
- nearestKeyDistance = distance;
- }
+ const float distance = getPointToKeyByIdLength(
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j);
+ if (distance < nearestKeyDistance) {
+ nearestKeyDistance = distance;
}
}
@@ -744,27 +734,23 @@ namespace latinime {
// Summing up probability densities of all near keys.
float sumOfProbabilityDensities = 0.0f;
for (int j = 0; j < keyCount; ++j) {
- if ((*sampledNearKeySets)[i].test(j)) {
- sumOfProbabilityDensities += distribution.getProbabilityDensity(
- proximityInfo->getKeyCenterXOfKeyIdG(j,
- NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */),
- proximityInfo->getKeyCenterYOfKeyIdG(j,
- NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */));
- }
+ sumOfProbabilityDensities += distribution.getProbabilityDensity(
+ proximityInfo->getKeyCenterXOfKeyIdG(j,
+ NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */),
+ proximityInfo->getKeyCenterYOfKeyIdG(j,
+ NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */));
}
// Split the probability of an input point to keys that are close to the input point.
for (int j = 0; j < keyCount; ++j) {
- if ((*sampledNearKeySets)[i].test(j)) {
- const float probabilityDensity = distribution.getProbabilityDensity(
- proximityInfo->getKeyCenterXOfKeyIdG(j,
- NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */),
- proximityInfo->getKeyCenterYOfKeyIdG(j,
- NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */));
- const float probability = inputCharProbability * probabilityDensity
- / sumOfProbabilityDensities;
- (*charProbabilities)[i][j] = probability;
- }
+ const float probabilityDensity = distribution.getProbabilityDensity(
+ proximityInfo->getKeyCenterXOfKeyIdG(j,
+ NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */),
+ proximityInfo->getKeyCenterYOfKeyIdG(j,
+ NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */));
+ const float probability = inputCharProbability * probabilityDensity
+ / sumOfProbabilityDensities;
+ (*charProbabilities)[i][j] = probability;
}
}
@@ -820,10 +806,9 @@ namespace latinime {
for (int j = 0; j < keyCount; ++j) {
hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].find(j);
if (it == (*charProbabilities)[i].end()){
- (*sampledNearKeySets)[i].reset(j);
+ continue;
} else if(it->second < ProximityInfoParams::MIN_PROBABILITY) {
// Erases from near keys vector because it has very low probability.
- (*sampledNearKeySets)[i].reset(j);
(*charProbabilities)[i].erase(j);
} else {
it->second = -logf(it->second);
@@ -835,9 +820,8 @@ namespace latinime {
/* static */ void ProximityInfoStateUtils::updateSampledSearchKeySets(
const ProximityInfo *const proximityInfo, const int sampledInputSize,
- const int lastSavedInputSize,
- const std::vector<int> *const sampledLengthCache,
- const std::vector<NearKeycodesSet> *const sampledNearKeySets,
+ const int lastSavedInputSize, const std::vector<int> *const sampledLengthCache,
+ const std::vector<hash_map_compat<int, float> > *const charProbabilities,
std::vector<NearKeycodesSet> *sampledSearchKeySets,
std::vector<std::vector<int> > *sampledSearchKeyVectors) {
sampledSearchKeySets->resize(sampledInputSize);
@@ -854,7 +838,12 @@ namespace latinime {
if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) {
break;
}
- (*sampledSearchKeySets)[i] |= (*sampledNearKeySets)[j];
+ for(const auto& charProbability : charProbabilities->at(j)) {
+ if (charProbability.first == NOT_AN_INDEX) {
+ continue;
+ }
+ (*sampledSearchKeySets)[i].set(charProbability.first);
+ }
}
}
const int keyCount = proximityInfo->getKeyCount();
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h
index 5d7a9c589..7aa20c3d1 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h
@@ -71,13 +71,12 @@ class ProximityInfoStateUtils {
const std::vector<float> *const sampledSpeedRates,
const std::vector<int> *const sampledLengthCache,
const std::vector<float> *const sampledNormalizedSquaredLengthCache,
- std::vector<NearKeycodesSet> *sampledNearKeySets,
const ProximityInfo *const proximityInfo,
std::vector<hash_map_compat<int, float> > *charProbabilities);
static void updateSampledSearchKeySets(const ProximityInfo *const proximityInfo,
const int sampledInputSize, const int lastSavedInputSize,
const std::vector<int> *const sampledLengthCache,
- const std::vector<NearKeycodesSet> *const sampledNearKeySets,
+ const std::vector<hash_map_compat<int, float> > *const charProbabilities,
std::vector<NearKeycodesSet> *sampledSearchKeySets,
std::vector<std::vector<int> > *sampledSearchKeyVectors);
static float getPointToKeyByIdLength(const float maxPointToKeyLength,
@@ -87,7 +86,6 @@ class ProximityInfoStateUtils {
const int sampledInputSize, const int lastSavedInputSize, const bool isGeometric,
const std::vector<int> *const sampledInputXs,
const std::vector<int> *const sampledInputYs,
- std::vector<NearKeycodesSet> *sampledNearKeySets,
std::vector<float> *sampledNormalizedSquaredLengthCache);
static void initPrimaryInputWord(const int inputSize, const int *const inputProximities,
int *primaryInputWord);
diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h
index d3b8da0cc..8ddaa0514 100644
--- a/native/jni/src/suggest/core/policy/traversal.h
+++ b/native/jni/src/suggest/core/policy/traversal.h
@@ -45,6 +45,7 @@ class Traversal {
virtual float getMaxSpatialDistance() const = 0;
virtual int getDefaultExpandDicNodeSize() const = 0;
virtual int getMaxCacheSize(const int inputSize) const = 0;
+ virtual int getTerminalCacheSize() const = 0;
virtual bool isPossibleOmissionChildNode(const DicTraverseSession *const traverseSession,
const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0;
virtual bool isGoodToTraverseNextWord(const DicNode *const dicNode) const = 0;
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 433820a42..e675e0bb3 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -88,7 +88,7 @@ void Suggest::initializeSearch(DicTraverseSession *traverseSession) const {
} else {
// Restart recognition at the root.
traverseSession->resetCache(TRAVERSAL->getMaxCacheSize(traverseSession->getInputSize()),
- MAX_RESULTS);
+ TRAVERSAL->getTerminalCacheSize());
// Create a new dic node here
DicNode rootNode;
DicNodeUtils::initAsRoot(traverseSession->getDictionaryStructurePolicy(),
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
index 5ba8bfa01..cb3dfac70 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
@@ -146,6 +146,10 @@ class TypingTraversal : public Traversal {
: ScoringParams::MAX_CACHE_DIC_NODE_SIZE;
}
+ AK_FORCE_INLINE int getTerminalCacheSize() const {
+ return MAX_RESULTS;
+ }
+
AK_FORCE_INLINE bool isPossibleOmissionChildNode(
const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
const DicNode *const dicNode) const {
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
index 57d295058..18390b2cd 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
@@ -25,8 +25,8 @@ import java.util.ArrayList;
@SmallTest
public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase {
- private static final int NUMBER_OF_SUBTYPES = 68;
- private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 43;
+ private static final int NUMBER_OF_SUBTYPES = 69;
+ private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 44;
private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2;
private static String toString(final ArrayList<InputMethodSubtype> subtypeList) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
new file mode 100644
index 000000000..75aad136f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Spanish;
+
+import java.util.Locale;
+
+/**
+ * es_419: Spanish (Latin America)/spanish
+ */
+@SmallTest
+public class TestsSpanish419 extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("es", "419");
+ private static final LayoutBase LAYOUT = new Spanish(new SpanishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
index e55c32bd0..2a4ead383 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -308,4 +308,68 @@ public class StringAndJsonUtilsTests extends AndroidTestCase {
assertEquals(objs[i], newObjArray.get(i));
}
}
+
+ public void testToCodePointArray() {
+ final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh\u0000\u2002\u2003\u3000xx";
+ final int[] EXPECTED_RESULT = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h',
+ 0, 0x2002, 0x2003, 0x3000, 'x', 'x'};
+ final int[] codePointArray = StringUtils.toCodePointArray(STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length());
+ assertEquals("toCodePointArray, size matches", codePointArray.length,
+ EXPECTED_RESULT.length);
+ for (int i = 0; i < EXPECTED_RESULT.length; ++i) {
+ assertEquals("toCodePointArray position " + i, codePointArray[i], EXPECTED_RESULT[i]);
+ }
+ }
+
+ public void testCopyCodePointsAndReturnCodePointCount() {
+ final String STR_WITH_SUPPLEMENTARY_CHAR = "AbcDE\uD861\uDED7fGh\u0000\u2002\u3000あx";
+ final int[] EXPECTED_RESULT = new int[] { 'A', 'b', 'c', 'D', 'E', 0x286D7,
+ 'f', 'G', 'h', 0, 0x2002, 0x3000, 'あ', 'x'};
+ final int[] EXPECTED_RESULT_DOWNCASE = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7,
+ 'f', 'g', 'h', 0, 0x2002, 0x3000, 'あ', 'x'};
+
+ int[] codePointArray = new int[50];
+ int codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length(), false /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount,
+ EXPECTED_RESULT.length);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT[i]);
+ }
+
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length(), true /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount downcase, size matches", codePointCount,
+ EXPECTED_RESULT_DOWNCASE.length);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT_DOWNCASE[i]);
+ }
+
+ final int JAVA_CHAR_COUNT = 8;
+ final int CODEPOINT_COUNT = 7;
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount,
+ CODEPOINT_COUNT);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT[i]);
+ }
+
+ boolean exceptionHappened = false;
+ codePointArray = new int[5];
+ try {
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ exceptionHappened = true;
+ }
+ assertTrue("copyCodePointsAndReturnCodePointCount throws when array is too small",
+ exceptionHappened);
+ }
}