aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java3
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java30
-rw-r--r--native/jni/src/proximity_info.cpp33
-rw-r--r--native/jni/src/proximity_info.h6
-rw-r--r--native/jni/src/proximity_info_state.h2
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java96
6 files changed, 102 insertions, 68 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 78c65e0c7..b3f7e674d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -825,7 +825,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// we know for sure the cursor moved while we were composing and we should reset
// the state.
final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
- if (!mExpectingUpdateSelection) {
+ if (!mExpectingUpdateSelection
+ && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
// TAKE CARE: there is a race condition when we enter this test even when the user
// did not explicitly move the cursor. This happens when typing fast, where two keys
// turn this flag on in succession and both onUpdateSelection() calls arrive after
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 37e1dbb69..ce7049f4f 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -629,4 +629,34 @@ public class RichInputConnection {
commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
return true;
}
+
+ /**
+ * Heuristic to determine if this is an expected update of the cursor.
+ *
+ * Sometimes updates to the cursor position are late because of their asynchronous nature.
+ * This method tries to determine if this update is one, based on the values of the cursor
+ * position in the update, and the currently expected position of the cursor according to
+ * LatinIME's internal accounting. If this is not a belated expected update, then it should
+ * mean that the user moved the cursor explicitly.
+ * This is quite robust, but of course it's not perfect. In particular, it will fail in the
+ * case we get an update A, the user types in N characters so as to move the cursor to A+N but
+ * we don't get those, and then the user places the cursor between A and A+N, and we get only
+ * this update and not the ones in-between. This is almost impossible to achieve even trying
+ * very very hard.
+ *
+ * @param oldSelStart The value of the old cursor position in the update.
+ * @param newSelStart The value of the new cursor position in the update.
+ * @return whether this is a belated expected update or not.
+ */
+ public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart) {
+ // If this is an update that arrives at our expected position, it's a belated update.
+ if (newSelStart == mCurrentCursorPosition) return true;
+ // If this is an update that moves the cursor from our expected position, it must be
+ // an explicit move.
+ if (oldSelStart == mCurrentCursorPosition) return false;
+ // The following returns true if newSelStart is between oldSelStart and
+ // mCurrentCursorPosition. We assume that if the updated position is between the old
+ // position and the expected position, then it must be a belated update.
+ return (newSelStart - oldSelStart) * (mCurrentCursorPosition - newSelStart) >= 0;
+ }
}
diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp
index 693a9f2b1..c9f83b62c 100644
--- a/native/jni/src/proximity_info.cpp
+++ b/native/jni/src/proximity_info.cpp
@@ -67,7 +67,8 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int ma
&& keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
&& sweetSpotCenterYs && sweetSpotRadii),
mProximityCharsArray(new int32_t[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE
- /* proximityGridLength */]) {
+ /* proximityGridLength */]),
+ mCodeToKeyMap() {
const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
if (DEBUG_PROXIMITY_INFO) {
AKLOGI("Create proximity info array %d", proximityGridLength);
@@ -88,22 +89,9 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int ma
safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
- initializeCodePointToKeyIndex();
initializeG();
}
-// Build the reversed look up table from the char code to the index in mKeyXCoordinates,
-// mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
-void ProximityInfo::initializeCodePointToKeyIndex() {
- memset(mCodePointToKeyIndex, -1, sizeof(mCodePointToKeyIndex));
- for (int i = 0; i < KEY_COUNT; ++i) {
- const int code = mKeyCodePoints[i];
- if (0 <= code && code <= MAX_CHAR_CODE) {
- mCodePointToKeyIndex[code] = i;
- }
- }
-}
-
ProximityInfo::~ProximityInfo() {
delete[] mProximityCharsArray;
}
@@ -239,11 +227,12 @@ int ProximityInfo::getKeyIndexOf(const int c) const {
// We do not have the coordinate data
return NOT_AN_INDEX;
}
- const unsigned short baseLowerC = toBaseLowerCase(c);
- if (baseLowerC > MAX_CHAR_CODE) {
- return NOT_AN_INDEX;
+ const int baseLowerC = static_cast<int>(toBaseLowerCase(c));
+ hash_map_compat<int, int>::const_iterator mapPos = mCodeToKeyMap.find(baseLowerC);
+ if (mapPos != mCodeToKeyMap.end()) {
+ return mapPos->second;
}
- return mCodePointToKeyIndex[baseLowerC];
+ return NOT_AN_INDEX;
}
int ProximityInfo::getCodePointOf(const int keyIndex) const {
@@ -260,12 +249,8 @@ void ProximityInfo::initializeG() {
const int lowerCode = toBaseLowerCase(code);
mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
- if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) {
- mCodePointToKeyIndex[lowerCode] = i;
- mKeyIndexToCodePointG[i] = lowerCode;
- } else {
- mKeyIndexToCodePointG[i] = code;
- }
+ mCodeToKeyMap[lowerCode] = i;
+ mKeyIndexToCodePointG[i] = lowerCode;
}
for (int i = 0; i < KEY_COUNT; i++) {
mKeyKeyDistancesG[i][i] = 0;
diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h
index 45df6ff6a..2f258ef86 100644
--- a/native/jni/src/proximity_info.h
+++ b/native/jni/src/proximity_info.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include "defines.h"
+#include "hash_map_compat.h"
#include "jni.h"
namespace latinime {
@@ -112,12 +113,9 @@ class ProximityInfo {
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfo);
- // The upper limit of the char code in mCodePointToKeyIndex
- static const int MAX_CHAR_CODE = 127;
static const float NOT_A_DISTANCE_FLOAT;
int getStartIndexFromCoordinates(const int x, const int y) const;
- void initializeCodePointToKeyIndex();
void initializeG();
float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const;
float calculateSquaredDistanceFromSweetSpotCenter(
@@ -154,7 +152,7 @@ class ProximityInfo {
float mSweetSpotCenterXs[MAX_KEY_COUNT_IN_A_KEYBOARD];
float mSweetSpotCenterYs[MAX_KEY_COUNT_IN_A_KEYBOARD];
float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD];
- int mCodePointToKeyIndex[MAX_CHAR_CODE + 1];
+ hash_map_compat<int, int> mCodeToKeyMap;
int mKeyIndexToCodePointG[MAX_KEY_COUNT_IN_A_KEYBOARD];
int mCenterXsG[MAX_KEY_COUNT_IN_A_KEYBOARD];
diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h
index fce4b5bdc..453b1de4d 100644
--- a/native/jni/src/proximity_info_state.h
+++ b/native/jni/src/proximity_info_state.h
@@ -37,8 +37,6 @@ class ProximityInfoState {
static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
- // The upper limit of the char code in mCodeToKeyIndex
- static const int MAX_CHAR_CODE = 127;
static const float NOT_A_DISTANCE_FLOAT = -1.0f;
static const int NOT_A_CODE = -1;
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
index 5d9c229e3..328784b1a 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
@@ -64,6 +64,10 @@ public class BinaryDictIOTests extends AndroidTestCase {
CollectionUtils.newSparseArray();
private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2);
+ private static final FormatSpec.FormatOptions VERSION3_WITHOUT_PARENTADDRESS =
+ new FormatSpec.FormatOptions(3, false);
+ private static final FormatSpec.FormatOptions VERSION3_WITH_PARENTADDRESS =
+ new FormatSpec.FormatOptions(3, true);
private static final String[] CHARACTERS = {
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
@@ -80,7 +84,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
for (int i = 0; i < sWords.size(); ++i) {
sChainBigrams.put(i, new ArrayList<Integer>());
if (i > 0) {
- sChainBigrams.get(i-1).add(i);
+ sChainBigrams.get(i - 1).add(i);
}
}
@@ -95,7 +99,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
/**
* Makes new buffer according to BUFFER_TYPE.
*/
- private FusionDictionaryBufferInterface getBuffer(final File file,final int bufferType) {
+ private FusionDictionaryBufferInterface getBuffer(final File file, final int bufferType) {
FileInputStream inStream = null;
try {
inStream = new FileInputStream(file);
@@ -173,7 +177,8 @@ public class BinaryDictIOTests extends AndroidTestCase {
}
}
- private long timeWritingDictToFile(final File file, final FusionDictionary dict) {
+ private long timeWritingDictToFile(final File file, final FusionDictionary dict,
+ final FormatSpec.FormatOptions formatOptions) {
long now = -1, diff = -1;
@@ -181,7 +186,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
final FileOutputStream out = new FileOutputStream(file);
now = System.currentTimeMillis();
- BinaryDictInputOutput.writeDictionaryBinary(out, dict, VERSION2);
+ BinaryDictInputOutput.writeDictionaryBinary(out, dict, formatOptions);
diff = System.currentTimeMillis() - now;
out.flush();
@@ -226,6 +231,14 @@ public class BinaryDictIOTests extends AndroidTestCase {
}
}
+ private String outputOptions(final int bufferType,
+ final FormatSpec.FormatOptions formatOptions) {
+ String result = " : buffer type = "
+ + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array");
+ result += " : version = " + formatOptions.mVersion;
+ return result + ", hasParentAddress = " + formatOptions.mHasParentAddress;
+ }
+
// Tests for readDictionaryBinary and writeDictionaryBinary
private long timeReadingAndCheckDict(final File file, final List<String> words,
@@ -243,7 +256,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
} catch (IOException e) {
Log.e(TAG, "IOException while reading dictionary: " + e);
} catch (UnsupportedFormatException e) {
- Log.e(TAG, "Unsupported format: "+ e);
+ Log.e(TAG, "Unsupported format: " + e);
}
checkDictionary(dict, words, bigrams, shortcutMap);
@@ -253,7 +266,8 @@ public class BinaryDictIOTests extends AndroidTestCase {
// Tests for readDictionaryBinary and writeDictionaryBinary
private String runReadAndWrite(final List<String> words,
final SparseArray<List<Integer>> bigrams, final Map<String, List<String>> shortcuts,
- final int bufferType, final String message) {
+ final int bufferType, final FormatSpec.FormatOptions formatOptions,
+ final String message) {
File file = null;
try {
file = File.createTempFile("runReadAndWrite", ".dict");
@@ -263,28 +277,34 @@ public class BinaryDictIOTests extends AndroidTestCase {
assertNotNull(file);
final FusionDictionary dict = new FusionDictionary(new Node(),
- new FusionDictionary.DictionaryOptions(
- new HashMap<String,String>(), false, false));
+ new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
addUnigrams(words.size(), dict, words, shortcuts);
addBigrams(dict, words, bigrams);
checkDictionary(dict, words, bigrams, shortcuts);
- final long write = timeWritingDictToFile(file, dict);
+ final long write = timeWritingDictToFile(file, dict, formatOptions);
final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType);
- return "PROF: read=" + read + "ms, write=" + write + "ms :" + message +
- " : buffer type = " + bufferType;
+ return "PROF: read=" + read + "ms, write=" + write + "ms :" + message
+ + " : " + outputOptions(bufferType, formatOptions);
+ }
+
+ private void runReadAndWriteTests(final List<String> results, final int bufferType,
+ final FormatSpec.FormatOptions formatOptions) {
+ results.add(runReadAndWrite(sWords, sEmptyBigrams, null /* shortcuts */, bufferType,
+ formatOptions, "unigram"));
+ results.add(runReadAndWrite(sWords, sChainBigrams, null /* shortcuts */, bufferType,
+ formatOptions, "chain"));
+ results.add(runReadAndWrite(sWords, sStarBigrams, null /* shortcuts */, bufferType,
+ formatOptions, "star"));
}
public void testReadAndWriteWithByteBuffer() {
final List<String> results = CollectionUtils.newArrayList();
- results.add(runReadAndWrite(sWords, sEmptyBigrams, null /* shortcuts */, USE_BYTE_BUFFER,
- "unigram"));
- results.add(runReadAndWrite(sWords, sChainBigrams, null /* shortcuts */, USE_BYTE_BUFFER,
- "chain"));
- results.add(runReadAndWrite(sWords, sStarBigrams, null /* shortcuts */, USE_BYTE_BUFFER,
- "star"));
+ runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2);
+ runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_PARENTADDRESS);
+ runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_PARENTADDRESS);
for (final String result : results) {
Log.d(TAG, result);
@@ -294,12 +314,9 @@ public class BinaryDictIOTests extends AndroidTestCase {
public void testReadAndWriteWithByteArray() {
final List<String> results = CollectionUtils.newArrayList();
- results.add(runReadAndWrite(sWords, sEmptyBigrams, null /* shortcuts */, USE_BYTE_ARRAY,
- "unigram"));
- results.add(runReadAndWrite(sWords, sChainBigrams, null /* shortcuts */, USE_BYTE_ARRAY,
- "chain"));
- results.add(runReadAndWrite(sWords, sStarBigrams, null /* shortcuts */, USE_BYTE_ARRAY,
- "star"));
+ runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2);
+ runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_PARENTADDRESS);
+ runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_PARENTADDRESS);
for (final String result : results) {
Log.d(TAG, result);
@@ -391,7 +408,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
private String runReadUnigramsAndBigramsBinary(final List<String> words,
final SparseArray<List<Integer>> bigrams, final int bufferType,
- final String message) {
+ final FormatSpec.FormatOptions formatOptions, final String message) {
File file = null;
try {
file = File.createTempFile("runReadUnigrams", ".dict");
@@ -407,25 +424,32 @@ public class BinaryDictIOTests extends AndroidTestCase {
addUnigrams(words.size(), dict, words, null /* shortcutMap */);
addBigrams(dict, words, bigrams);
- timeWritingDictToFile(file, dict);
+ timeWritingDictToFile(file, dict, formatOptions);
long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType);
long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */,
bufferType);
return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap
- + " : " + message + " : buffer type = " + bufferType;
+ + " : " + message + " : " + outputOptions(bufferType, formatOptions);
+ }
+
+ private void runReadUnigramsAndBigramsTests(final List<String> results, final int bufferType,
+ final FormatSpec.FormatOptions formatOptions) {
+ results.add(runReadUnigramsAndBigramsBinary(sWords, sEmptyBigrams, bufferType,
+ formatOptions, "unigram"));
+ results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType,
+ formatOptions, "chain"));
+ results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType,
+ formatOptions, "star"));
}
public void testReadUnigramsAndBigramsBinaryWithByteBuffer() {
final List<String> results = CollectionUtils.newArrayList();
- results.add(runReadUnigramsAndBigramsBinary(sWords, sEmptyBigrams, USE_BYTE_BUFFER,
- "unigram"));
- results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, USE_BYTE_BUFFER,
- "chain"));
- results.add(runReadUnigramsAndBigramsBinary(sWords, sStarBigrams, USE_BYTE_BUFFER,
- "star"));
+ runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2);
+ runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_PARENTADDRESS);
+ runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_PARENTADDRESS);
for (final String result : results) {
Log.d(TAG, result);
@@ -435,11 +459,9 @@ public class BinaryDictIOTests extends AndroidTestCase {
public void testReadUnigramsAndBigramsBinaryWithByteArray() {
final List<String> results = CollectionUtils.newArrayList();
- results.add(runReadUnigramsAndBigramsBinary(sWords, sEmptyBigrams, USE_BYTE_ARRAY,
- "unigram"));
- results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, USE_BYTE_ARRAY,
- "chain"));
- results.add(runReadUnigramsAndBigramsBinary(sWords, sStarBigrams, USE_BYTE_ARRAY, "star"));
+ runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2);
+ runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_PARENTADDRESS);
+ runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_PARENTADDRESS);
for (final String result : results) {
Log.d(TAG, result);