diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 3005920b1..06d44680d 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -174,7 +174,7 @@ public class Keyboard { mDefaultHeight = mDefaultWidth; mId = id; loadKeyboard(context, xmlLayoutResId); - mProximityInfo = new ProximityInfo(mDisplayWidth, mDisplayHeight, GRID_WIDTH, GRID_HEIGHT); + mProximityInfo = new ProximityInfo(GRID_WIDTH, GRID_HEIGHT); } public int getProximityInfo() { @@ -378,7 +378,7 @@ public class Keyboard { mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell; } } - mProximityInfo.setProximityInfo(mGridNeighbors); + mProximityInfo.setProximityInfo(mGridNeighbors, getMinWidth(), getHeight(), mKeys); } public boolean isInside(Key key, int x, int y) { diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index fcada3762..80d6de952 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -18,18 +18,17 @@ package com.android.inputmethod.keyboard; import com.android.inputmethod.latin.Utils; +import java.util.Arrays; +import java.util.List; + public class ProximityInfo { public static final int MAX_PROXIMITY_CHARS_SIZE = 16; - private final int mDisplayWidth; - private final int mDisplayHeight; private final int mGridWidth; private final int mGridHeight; private final int mGridSize; - ProximityInfo(int displayWidth, int displayHeight, int gridWidth, int gridHeight) { - mDisplayWidth = displayWidth; - mDisplayHeight = displayHeight; + ProximityInfo(int gridWidth, int gridHeight) { mGridWidth = gridWidth; mGridHeight = gridHeight; mGridSize = mGridWidth * mGridHeight; @@ -43,20 +42,19 @@ public class ProximityInfo { int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray); private native void releaseProximityInfoNative(int nativeProximityInfo); - public final void setProximityInfo(int[][] gridNeighbors) { + public final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth, + int keyboardHeight, List keys) { int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; + Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE); for (int i = 0; i < mGridSize; ++i) { - final int proximityCharsLength = gridNeighbors[i].length; - for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE; ++j) { - int charCode = KeyDetector.NOT_A_KEY; - if (j < proximityCharsLength) { - charCode = gridNeighbors[i][j]; - } - proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] = charCode; + final int proximityCharsLength = gridNeighborKeyIndexes[i].length; + for (int j = 0; j < proximityCharsLength; ++j) { + proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] = + keys.get(gridNeighborKeyIndexes[i][j]).mCode; } } mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE, - mDisplayWidth, mDisplayHeight, mGridWidth, mGridHeight, proximityCharsArray); + keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray); } // TODO: Get rid of this function's input (keyboard). diff --git a/native/src/defines.h b/native/src/defines.h index 5d85b7c51..9534f8a87 100644 --- a/native/src/defines.h +++ b/native/src/defines.h @@ -129,10 +129,13 @@ static void prof_out(void) { #define DICTIONARY_HEADER_SIZE 2 #define NOT_VALID_WORD -99 +#define KEYCODE_SPACE ' ' + #define SUGGEST_WORDS_WITH_MISSING_CHARACTER true #define SUGGEST_WORDS_WITH_MISSING_SPACE_CHARACTER true #define SUGGEST_WORDS_WITH_EXCESSIVE_CHARACTER true #define SUGGEST_WORDS_WITH_TRANSPOSED_CHARACTERS true +#define SUGGEST_WORDS_WITH_SPACE_PROXIMITY true // The following "rate"s are used as a multiplier before dividing by 100, so they are in percent. #define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 75 diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp index d0cba3eb6..5f2d09f5c 100644 --- a/native/src/proximity_info.cpp +++ b/native/src/proximity_info.cpp @@ -17,18 +17,48 @@ #include #include +#define LOG_TAG "LatinIME: proximity_info.cpp" + #include "proximity_info.h" namespace latinime { -ProximityInfo::ProximityInfo(int maxProximityCharsSize, int displayWidth, int displayHeight, - int gridWidth, int gridHeight, uint32_t const *proximityCharsArray) - : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), DISPLAY_WIDTH(displayWidth), - DISPLAY_HEIGHT(displayHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight) { - mProximityCharsArray = new uint32_t[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE]; - memcpy(mProximityCharsArray, proximityCharsArray, sizeof(mProximityCharsArray)); +ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth, + const int keyboardHeight, const int gridWidth, const int gridHeight, + const uint32_t *proximityCharsArray) + : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth), + KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), + CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), + CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight) { + const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; + mProximityCharsArray = new uint32_t[len]; + if (DEBUG_PROXIMITY_INFO) { + LOGI("Create proximity info array %d", len); + } + memcpy(mProximityCharsArray, proximityCharsArray, len * sizeof(mProximityCharsArray[0])); } ProximityInfo::~ProximityInfo() { delete[] mProximityCharsArray; } + +inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const { + return (y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH) + * MAX_PROXIMITY_CHARS_SIZE; } + +bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { + const int startIndex = getStartIndexFromCoordinates(x, y); + if (DEBUG_PROXIMITY_INFO) { + LOGI("hasSpaceProximity: index %d", startIndex); + } + for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { + if (DEBUG_PROXIMITY_INFO) { + LOGI("Index: %d", mProximityCharsArray[startIndex + i]); + } + if (mProximityCharsArray[startIndex + i] == KEYCODE_SPACE) { + return true; + } + } + return false; +} +} // namespace latinime diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h index 54e995059..0f1201866 100644 --- a/native/src/proximity_info.h +++ b/native/src/proximity_info.h @@ -25,15 +25,20 @@ namespace latinime { class ProximityInfo { public: - ProximityInfo(int maxProximityCharsSize, int displayWidth, int displayHeight, int gridWidth, - int gridHeight, uint32_t const *proximityCharsArray); + ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth, + const int keybaordHeight, const int gridWidth, const int gridHeight, + const uint32_t *proximityCharsArray); ~ProximityInfo(); + bool hasSpaceProximity(const int x, const int y) const; private: - const int MAX_PROXIMITY_CHARS_SIZE; - const int DISPLAY_WIDTH; - const int DISPLAY_HEIGHT; + int getStartIndexFromCoordinates(const int x, const int y) const; + const int CELL_WIDTH; + const int CELL_HEIGHT; + const int KEYBOARD_WIDTH; + const int KEYBOARD_HEIGHT; const int GRID_WIDTH; const int GRID_HEIGHT; + const int MAX_PROXIMITY_CHARS_SIZE; uint32_t *mProximityCharsArray; }; }; // namespace latinime diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index b82910a40..274e1f6d3 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -142,7 +142,7 @@ int UnigramDictionary::getSuggestions(const ProximityInfo *proximityInfo, const outWords, frequencies); } - PROF_START(6); + PROF_START(20); // Get the word count int suggestedWordsCount = 0; while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) { @@ -158,7 +158,7 @@ int UnigramDictionary::getSuggestions(const ProximityInfo *proximityInfo, const } } } - PROF_END(6); + PROF_END(20); PROF_CLOSE; return suggestedWordsCount; } @@ -172,12 +172,6 @@ void UnigramDictionary::getWordSuggestions(const ProximityInfo *proximityInfo, initSuggestions(codes, codesSize, outWords, frequencies); if (DEBUG_DICT) assert(codesSize == mInputLength); - if (DEBUG_PROXIMITY_INFO) { - for (int i = 0; i < codesSize; ++i) { - LOGI("Input[%d] x = %d, y = %d", i, xcoordinates[i], ycoordinates[i]); - } - } - const int MAX_DEPTH = min(mInputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH); PROF_END(0); @@ -227,6 +221,25 @@ void UnigramDictionary::getWordSuggestions(const ProximityInfo *proximityInfo, } } PROF_END(5); + + PROF_START(6); + if (SUGGEST_WORDS_WITH_SPACE_PROXIMITY) { + // The first and last "mistyped spaces" are taken care of by excessive character handling + for (int i = 1; i < codesSize - 1; ++i) { + if (DEBUG_DICT) LOGI("--- Suggest words with proximity space %d", i); + const int x = xcoordinates[i]; + const int y = ycoordinates[i]; + if (DEBUG_PROXIMITY_INFO) + LOGI("Input[%d] x = %d, y = %d, has space proximity = %d", + i, x, y, proximityInfo->hasSpaceProximity(x, y)); + + if (proximityInfo->hasSpaceProximity(x, y)) { + getMistypedSpaceWords(mInputLength, i); + } + + } + } + PROF_END(6); } void UnigramDictionary::initSuggestions(const int *codes, const int codesSize, @@ -387,27 +400,31 @@ inline static void multiplyRate(const int rate, int *freq) { } } -bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int missingSpacePos) { - if (missingSpacePos <= 0 || missingSpacePos >= inputLength - || inputLength >= MAX_WORD_LENGTH) return false; - const int newWordLength = inputLength + 1; +bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength, + const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos, + const int secondWordLength) { + if (inputLength >= MAX_WORD_LENGTH) return false; + if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos + || firstWordStartPos < 0 || secondWordStartPos >= inputLength) + return false; + const int newWordLength = firstWordLength + secondWordLength + 1; // Allocating variable length array on stack unsigned short word[newWordLength]; - const int firstFreq = getBestWordFreq(0, missingSpacePos, mWord); + const int firstFreq = getBestWordFreq(firstWordStartPos, firstWordLength, mWord); if (DEBUG_DICT) LOGI("First freq: %d", firstFreq); if (firstFreq <= 0) return false; - for (int i = 0; i < missingSpacePos; ++i) { + for (int i = 0; i < firstWordLength; ++i) { word[i] = mWord[i]; } - const int secondFreq = getBestWordFreq(missingSpacePos, inputLength - missingSpacePos, mWord); + const int secondFreq = getBestWordFreq(secondWordStartPos, secondWordLength, mWord); if (DEBUG_DICT) LOGI("Second freq: %d", secondFreq); if (secondFreq <= 0) return false; - word[missingSpacePos] = SPACE; - for (int i = (missingSpacePos + 1); i < newWordLength; ++i) { - word[i] = mWord[i - missingSpacePos - 1]; + word[firstWordLength] = SPACE; + for (int i = (firstWordLength + 1); i < newWordLength; ++i) { + word[i] = mWord[i - firstWordLength - 1]; } int pairFreq = ((firstFreq + secondFreq) / 2); @@ -417,6 +434,17 @@ bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int mi return true; } +bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int missingSpacePos) { + return getSplitTwoWordsSuggestion( + inputLength, 0, missingSpacePos, missingSpacePos, inputLength - missingSpacePos); +} + +bool UnigramDictionary::getMistypedSpaceWords(const int inputLength, const int spaceProximityPos) { + return getSplitTwoWordsSuggestion( + inputLength, 0, spaceProximityPos, spaceProximityPos + 1, + inputLength - spaceProximityPos - 1); +} + // Keep this for comparing spec to new getWords void UnigramDictionary::getWordsOld(const int initialPos, const int inputLength, const int skipPos, const int excessivePos, const int transposedPos,int *nextLetters, diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h index 8a4b40e0c..7359481a8 100644 --- a/native/src/unigram_dictionary.h +++ b/native/src/unigram_dictionary.h @@ -64,7 +64,11 @@ private: const bool traverseAllNodes, const int snr, const int inputIndex, const int diffs, const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters, const int nextLettersSize); + bool getSplitTwoWordsSuggestion(const int inputLength, + const int firstWordStartPos, const int firstWordLength, + const int secondWordStartPos, const int secondWordLength); bool getMissingSpaceWords(const int inputLength, const int missingSpacePos); + bool getMistypedSpaceWords(const int inputLength, const int spaceProximityPos); // Keep getWordsOld for comparing performance between getWords and getWordsOld void getWordsOld(const int initialPos, const int inputLength, const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters,