diff options
-rw-r--r-- | java/res/values/strings.xml | 3 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/SuggestedWords.java | 6 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/research/JsonUtils.java | 2 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/research/LogUnit.java | 11 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/research/ResearchLogger.java | 77 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/research/Statistics.java | 18 | ||||
-rw-r--r-- | native/jni/src/additional_proximity_chars.h | 4 | ||||
-rw-r--r-- | native/jni/src/proximity_info.cpp | 108 | ||||
-rw-r--r-- | native/jni/src/proximity_info.h | 25 | ||||
-rw-r--r-- | native/jni/src/proximity_info_state.cpp | 28 | ||||
-rw-r--r-- | native/jni/src/proximity_info_state.h | 12 | ||||
-rw-r--r-- | native/jni/src/proximity_info_utils.h | 192 |
12 files changed, 303 insertions, 183 deletions
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index fd7aac49f..f9d51ff69 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -269,8 +269,7 @@ <string name="research_feedback_dialog_title" translatable="false">Send feedback</string> <!-- Text for checkbox option to include user data in feedback for research purposes [CHAR LIMIT=50] --> <!-- TODO: remove translatable=false attribute once text is stable --> - <!-- TODO: handle multilingual plurals --> - <string name="research_feedback_include_history_label" translatable="false">Include last <xliff:g id="word">%d</xliff:g> words entered</string> + <string name="research_feedback_include_history_label" translatable="false">Include session history</string> <!-- Hint to user about the text entry field where they should enter research feedback [CHAR LIMIT=40] --> <!-- TODO: remove translatable=false attribute once text is stable --> <string name="research_feedback_hint" translatable="false">Enter your feedback here.</string> diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 572f2906e..b7ca60fec 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -65,10 +65,6 @@ public final class SuggestedWords { return mSuggestedWordInfoList.get(pos).mWord; } - public SuggestedWordInfo getWordInfo(int pos) { - return mSuggestedWordInfoList.get(pos); - } - public SuggestedWordInfo getInfo(int pos) { return mSuggestedWordInfoList.get(pos); } @@ -113,7 +109,7 @@ public final class SuggestedWords { alreadySeen.add(typedWord.toString()); final int previousSize = previousSuggestions.size(); for (int pos = 1; pos < previousSize; pos++) { - final SuggestedWordInfo prevWordInfo = previousSuggestions.getWordInfo(pos); + final SuggestedWordInfo prevWordInfo = previousSuggestions.getInfo(pos); final String prevWord = prevWordInfo.mWord; // Filter out duplicate suggestion. if (!alreadySeen.contains(prevWord)) { diff --git a/java/src/com/android/inputmethod/research/JsonUtils.java b/java/src/com/android/inputmethod/research/JsonUtils.java index 1dfd01c69..ceba08d47 100644 --- a/java/src/com/android/inputmethod/research/JsonUtils.java +++ b/java/src/com/android/inputmethod/research/JsonUtils.java @@ -98,7 +98,7 @@ import java.util.Map; jsonWriter.beginArray(); final int size = words.size(); for (int j = 0; j < size; j++) { - final SuggestedWordInfo wordInfo = words.getWordInfo(j); + final SuggestedWordInfo wordInfo = words.getInfo(j); jsonWriter.value(wordInfo.toString()); } jsonWriter.endArray(); diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index cfba28909..70bbf9dc0 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -60,6 +60,7 @@ import java.util.Map; private String mWord; private boolean mMayContainDigit; private boolean mIsPartOfMegaword; + private boolean mContainsCorrection; public LogUnit() { mLogStatementList = new ArrayList<LogStatement>(); @@ -274,6 +275,14 @@ import java.util.Map; return mMayContainDigit; } + public void setContainsCorrection() { + mContainsCorrection = true; + } + + public boolean containsCorrection() { + return mContainsCorrection; + } + public boolean isEmpty() { return mLogStatementList.isEmpty(); } @@ -301,6 +310,7 @@ import java.util.Map; true /* isPartOfMegaword */); newLogUnit.mWord = null; newLogUnit.mMayContainDigit = mMayContainDigit; + newLogUnit.mContainsCorrection = mContainsCorrection; // Purge the logStatements and associated data from this LogUnit. laterLogStatements.clear(); @@ -320,6 +330,7 @@ import java.util.Map; mTimeList.addAll(logUnit.mTimeList); mWord = null; mMayContainDigit = mMayContainDigit || logUnit.mMayContainDigit; + mContainsCorrection = mContainsCorrection || logUnit.mContainsCorrection; mIsPartOfMegaword = false; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index f4249a045..a46216c5e 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -106,7 +106,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // Change the default indicator to something very visible. Currently two red vertical bars on // either side of they keyboard. private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false || IS_LOGGING_EVERYTHING; - public static final int FEEDBACK_WORD_BUFFER_SIZE = 5; + // FEEDBACK_WORD_BUFFER_SIZE should add 1 because it must also hold the feedback LogUnit itself. + public static final int FEEDBACK_WORD_BUFFER_SIZE = (Integer.MAX_VALUE - 1) + 1; // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; @@ -391,9 +392,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } if (mFeedbackLogBuffer == null) { mFeedbackLog = new ResearchLog(createLogFile(mFilesDir), mLatinIME); - // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold - // the feedback LogUnit itself. - mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); + mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE); } } @@ -522,8 +521,25 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang */ private boolean mInFeedbackDialog = false; + + // The feedback dialog causes stop() to be called for the keyboard connected to the original + // window. This is because the feedback dialog must present its own EditText box that displays + // a keyboard. stop() normally causes mFeedbackLogBuffer, which contains the user's data, to be + // cleared, and causes mFeedbackLog, which is ready to collect information in case the user + // wants to upload, to be closed. This is good because we don't need to log information about + // what the user is typing in the feedback dialog, but bad because this data must be uploaded. + // Here we save the LogBuffer and Log so the feedback dialog can later access their data. + private LogBuffer mSavedFeedbackLogBuffer; + private ResearchLog mSavedFeedbackLog; + public void presentFeedbackDialog(LatinIME latinIME) { mInFeedbackDialog = true; + mSavedFeedbackLogBuffer = mFeedbackLogBuffer; + mSavedFeedbackLog = mFeedbackLog; + // Set the non-saved versions to null so that the stop() caused by switching to the + // Feedback dialog will not close them. + mFeedbackLogBuffer = null; + mFeedbackLog = null; latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class); } @@ -589,28 +605,25 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private static final LogStatement LOGSTATEMENT_FEEDBACK = - new LogStatement("UserTimestamp", false, false, "contents"); + new LogStatement("UserFeedback", false, false, "contents"); public void sendFeedback(final String feedbackContents, final boolean includeHistory) { - if (mFeedbackLogBuffer == null) { + if (mSavedFeedbackLogBuffer == null) { return; } - if (includeHistory) { - commitCurrentLogUnit(); - } else { - mFeedbackLogBuffer.clear(); + if (!includeHistory) { + mSavedFeedbackLogBuffer.clear(); } final LogUnit feedbackLogUnit = new LogUnit(); feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(), feedbackContents); mFeedbackLogBuffer.shiftIn(feedbackLogUnit); - publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); - mFeedbackLog.close(new Runnable() { + publishLogBuffer(mFeedbackLogBuffer, mSavedFeedbackLog, true /* isIncludingPrivateData */); + mSavedFeedbackLog.close(new Runnable() { @Override public void run() { uploadNow(); } }); - mFeedbackLog = new ResearchLog(createLogFile(mFilesDir), mLatinIME); } public void uploadNow() { @@ -643,13 +656,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isAllowedToLog() { - if (DEBUG) { - Log.d(TAG, "iatl: " + - "mipw=" + mIsPasswordView + - ", mils=" + mIsLoggingSuspended + - ", sil=" + sIsLogging + - ", mInFeedbackDialog=" + mInFeedbackDialog); - } return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog; } @@ -714,6 +720,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit.setMayContainDigit(); } + private void setCurrentLogUnitContainsCorrection() { + mCurrentLogUnit.setContainsCorrection(); + } + /* package for test */ void commitCurrentLogUnit() { if (DEBUG) { Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasWord() ? @@ -844,7 +854,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit.setWord(word); final boolean isDictionaryWord = dictionary != null && dictionary.isValidWord(word); - mStatistics.recordWordEntered(isDictionaryWord); + mStatistics.recordWordEntered(isDictionaryWord, mCurrentLogUnit.containsCorrection()); } final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime); enqueueCommitText(word, isBatchMode); @@ -852,6 +862,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit = newLogUnit; } + /** + * Record the time of a MotionEvent.ACTION_DOWN. + * + * Warning: Not thread safe. Only call from the main thread. + */ private void setSavedDownEventTime(final long time) { mSavedDownEventTime = time; } @@ -1170,6 +1185,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang scrubDigitsFromString(replacedWord), index, suggestion == null ? null : scrubbedWord, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); + researchLogger.setCurrentLogUnitContainsCorrection(); researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, Long.MAX_VALUE, isBatchMode); researchLogger.mStatistics.recordManualSuggestion(SystemClock.uptimeMillis()); } @@ -1329,6 +1345,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueueEvent(logUnit != null ? logUnit : researchLogger.mCurrentLogUnit, LOGSTATEMENT_LATINIME_REVERTCOMMIT, committedWord, originallyTypedWord, separatorString); + if (logUnit != null) { + logUnit.setContainsCorrection(); + } researchLogger.mStatistics.recordRevertCommit(SystemClock.uptimeMillis()); researchLogger.commitCurrentLogUnitAsWord(originallyTypedWord, Long.MAX_VALUE, isBatchMode); } @@ -1475,20 +1494,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private boolean isExpectingCommitText = false; /** - * Log a call to RichInputConnection.commitPartialText + * 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_LATINIME_COMMIT_PARTIAL_TEXT = - new LogStatement("LatinIMECommitPartialText", true, false, "newCursorPosition"); - public static void latinIME_commitPartialText(final String committedWord, + 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_LATINIME_COMMIT_PARTIAL_TEXT); + researchLogger.enqueueEvent(LOGSTATEMENT_COMMIT_PARTIAL_TEXT); + researchLogger.mStatistics.recordAutoCorrection(SystemClock.uptimeMillis()); researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, lastTimestampOfWordData, isBatchMode); } @@ -1729,7 +1749,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete", "dictionaryWordCount", "splitWordsCount", "gestureInputCount", "gestureCharsCount", "gesturesDeletedCount", "manualSuggestionsCount", - "revertCommitsCount"); + "revertCommitsCount", "correctedWordsCount", "autoCorrectionsCount"); private static void logStatistics() { final ResearchLogger researchLogger = getInstance(); final Statistics statistics = researchLogger.mStatistics; @@ -1743,6 +1763,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang statistics.mDictionaryWordCount, statistics.mSplitWordsCount, statistics.mGesturesInputCount, statistics.mGesturesCharsCount, statistics.mGesturesDeletedCount, statistics.mManualSuggestionsCount, - statistics.mRevertCommitsCount); + statistics.mRevertCommitsCount, statistics.mCorrectedWordsCount, + statistics.mAutoCorrectionsCount); } } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index a9202651e..f0cb1578c 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -25,6 +25,7 @@ public class Statistics { private static final String TAG = Statistics.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; + // TODO: Cleanup comments to only including those giving meaningful information. // Number of characters entered during a typing session int mCharCount; // Number of letter characters entered during a typing session @@ -41,6 +42,8 @@ public class Statistics { int mDictionaryWordCount; // Number of words split and spaces automatically entered. int mSplitWordsCount; + // Number of words entered during a session. + int mCorrectedWordsCount; // Number of gestures that were input. int mGesturesInputCount; // Number of gestures that were deleted. @@ -49,6 +52,8 @@ public class Statistics { int mGesturesCharsCount; // Number of manual suggestions chosen. int mManualSuggestionsCount; + // Number of times that autocorrection was invoked. + int mAutoCorrectionsCount; // Number of times a commit was reverted in this session. int mRevertCommitsCount; // Whether the text field was empty upon editing @@ -113,10 +118,12 @@ public class Statistics { mWordCount = 0; mDictionaryWordCount = 0; mSplitWordsCount = 0; + mCorrectedWordsCount = 0; mGesturesInputCount = 0; mGesturesDeletedCount = 0; mManualSuggestionsCount = 0; mRevertCommitsCount = 0; + mAutoCorrectionsCount = 0; mIsEmptyUponStarting = true; mIsEmptinessStateKnown = false; mKeyCounter.reset(); @@ -152,11 +159,15 @@ public class Statistics { } } - public void recordWordEntered(final boolean isDictionaryWord) { + public void recordWordEntered(final boolean isDictionaryWord, + final boolean containsCorrection) { mWordCount++; if (isDictionaryWord) { mDictionaryWordCount++; } + if (containsCorrection) { + mCorrectedWordsCount++; + } } public void recordSplitWords() { @@ -184,6 +195,11 @@ public class Statistics { recordUserAction(time, false /* isDeletion */); } + public void recordAutoCorrection(final long time) { + mAutoCorrectionsCount++; + recordUserAction(time, false /* isDeletion */); + } + public void recordRevertCommit(final long time) { mRevertCommitsCount++; recordUserAction(time, true /* isDeletion */); diff --git a/native/jni/src/additional_proximity_chars.h b/native/jni/src/additional_proximity_chars.h index 21eb0bf8d..a88fd6cea 100644 --- a/native/jni/src/additional_proximity_chars.h +++ b/native/jni/src/additional_proximity_chars.h @@ -45,7 +45,7 @@ class AdditionalProximityChars { } public: - static int getAdditionalCharsSize(const char *localeStr, const int c) { + static int getAdditionalCharsSize(const char *const localeStr, const int c) { if (!isEnLocale(localeStr)) { return 0; } @@ -65,7 +65,7 @@ class AdditionalProximityChars { } } - static const int *getAdditionalChars(const char *localeStr, const int c) { + static const int *getAdditionalChars(const char *const localeStr, const int c) { if (!isEnLocale(localeStr)) { return 0; } diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 9b99554d6..08646afa2 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -94,11 +94,6 @@ 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 { if (x < 0 || y < 0) { if (DEBUG_DICT) { @@ -109,7 +104,8 @@ bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { return false; } - const int startIndex = getStartIndexFromCoordinates(x, y); + const int startIndex = ProximityInfoUtils::getStartIndexFromCoordinates( + MAX_PROXIMITY_CHARS_SIZE, x, y, CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH); if (DEBUG_PROXIMITY_INFO) { AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y); } @@ -147,100 +143,6 @@ float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG( return getSquaredDistanceFloat(centerX, centerY, touchX, touchY) / SQUARE_FLOAT(keyWidth); } -int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const { - if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case - const int left = mKeyXCoordinates[keyId]; - const int top = mKeyYCoordinates[keyId]; - const int right = left + mKeyWidths[keyId]; - const int bottom = top + mKeyHeights[keyId]; - const int edgeX = x < left ? left : (x > right ? right : x); - const int edgeY = y < top ? top : (y > bottom ? bottom : y); - const int dx = x - edgeX; - const int dy = y - edgeY; - return dx * dx + dy * dy; -} - -void ProximityInfo::calculateNearbyKeyCodes( - const int x, const int y, const int primaryKey, int *inputCodes) const { - int *proximityCharsArray = mProximityCharsArray; - int insertPos = 0; - inputCodes[insertPos++] = primaryKey; - const int startIndex = getStartIndexFromCoordinates(x, y); - if (startIndex >= 0) { - for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { - const int c = proximityCharsArray[startIndex + i]; - if (c < KEYCODE_SPACE || c == primaryKey) { - continue; - } - const int keyIndex = getKeyIndexOf(c); - const bool onKey = isOnKey(keyIndex, x, y); - const int distance = squaredDistanceToEdge(keyIndex, x, y); - if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) { - inputCodes[insertPos++] = c; - if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) { - if (DEBUG_DICT) { - ASSERT(false); - } - return; - } - } - } - const int additionalProximitySize = - AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey); - if (additionalProximitySize > 0) { - inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE; - if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) { - if (DEBUG_DICT) { - ASSERT(false); - } - return; - } - - const int *additionalProximityChars = - AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey); - for (int j = 0; j < additionalProximitySize; ++j) { - const int ac = additionalProximityChars[j]; - int k = 0; - for (; k < insertPos; ++k) { - if (ac == inputCodes[k]) { - break; - } - } - if (k < insertPos) { - continue; - } - inputCodes[insertPos++] = ac; - if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) { - if (DEBUG_DICT) { - ASSERT(false); - } - return; - } - } - } - } - // Add a delimiter for the proximity characters - for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { - inputCodes[i] = NOT_A_CODE_POINT; - } -} - -int ProximityInfo::getKeyIndexOf(const int c) const { - if (KEY_COUNT == 0) { - // We do not have the coordinate data - return NOT_AN_INDEX; - } - if (c == NOT_A_CODE_POINT) { - return NOT_AN_INDEX; - } - const int lowerCode = toLowerCase(c); - hash_map_compat<int, int>::const_iterator mapPos = mCodeToKeyMap.find(lowerCode); - if (mapPos != mCodeToKeyMap.end()) { - return mapPos->second; - } - return NOT_AN_INDEX; -} - int ProximityInfo::getCodePointOf(const int keyIndex) const { if (keyIndex < 0 || keyIndex >= KEY_COUNT) { return NOT_A_CODE_POINT; @@ -269,11 +171,13 @@ void ProximityInfo::initializeG() { } int ProximityInfo::getKeyCenterXOfCodePointG(int charCode) const { - return getKeyCenterXOfKeyIdG(getKeyIndexOf(charCode)); + return getKeyCenterXOfKeyIdG( + ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, charCode, &mCodeToKeyMap)); } int ProximityInfo::getKeyCenterYOfCodePointG(int charCode) const { - return getKeyCenterYOfKeyIdG(getKeyIndexOf(charCode)); + return getKeyCenterYOfKeyIdG( + ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, charCode, &mCodeToKeyMap)); } int ProximityInfo::getKeyCenterXOfKeyIdG(int keyId) const { diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index d00228359..6be42a057 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -20,6 +20,7 @@ #include "defines.h" #include "hash_map_compat.h" #include "jni.h" +#include "proximity_info_utils.h" namespace latinime { @@ -40,7 +41,6 @@ class ProximityInfo { float getNormalizedSquaredDistanceFromCenterFloatG( const int keyId, const int x, const int y) const; bool sameAsTyped(const unsigned short *word, int length) const; - int getKeyIndexOf(const int c) const; int getCodePointOf(const int keyIndex) const; bool hasSweetSpotData(const int keyIndex) const { // When there are no calibration data for a key, @@ -109,23 +109,26 @@ class ProximityInfo { int getKeyCenterYOfKeyIdG(int keyId) const; int getKeyKeyDistanceG(int keyId0, int keyId1) const; + void initializeProximities(const int *const inputCodes, const int *const inputXCoordinates, + const int *const inputYCoordinates, const int inputSize, int *allInputCodes) const { + ProximityInfoUtils::initializeProximities(inputCodes, inputXCoordinates, inputYCoordinates, + inputSize, mKeyXCoordinates, mKeyYCoordinates, mKeyWidths, mKeyHeights, + mProximityCharsArray, MAX_PROXIMITY_CHARS_SIZE, CELL_HEIGHT, CELL_WIDTH, + GRID_WIDTH, MOST_COMMON_KEY_WIDTH, KEY_COUNT, mLocaleStr, &mCodeToKeyMap, + allInputCodes); + } + + int getKeyIndexOf(const int c) const { + return ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, c, &mCodeToKeyMap); + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfo); static const float NOT_A_DISTANCE_FLOAT; - int getStartIndexFromCoordinates(const int x, const int y) const; void initializeG(); float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const; bool hasInputCoordinates() const; - int squaredDistanceToEdge(const int keyId, const int x, const int y) const; - bool isOnKey(const int keyId, const int x, const int y) const { - if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case - const int left = mKeyXCoordinates[keyId]; - const int top = mKeyYCoordinates[keyId]; - const int right = left + mKeyWidths[keyId] + 1; - const int bottom = top + mKeyHeights[keyId]; - return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom; - } const int MAX_PROXIMITY_CHARS_SIZE; const int GRID_WIDTH; diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp index aa029297e..1e1413a5d 100644 --- a/native/jni/src/proximity_info_state.cpp +++ b/native/jni/src/proximity_info_state.cpp @@ -53,33 +53,11 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi mGridHeight = proximityInfo->getGridWidth(); mGridWidth = proximityInfo->getGridHeight(); - memset(mInputCodes, 0, sizeof(mInputCodes)); + memset(mInputProximities, 0, sizeof(mInputProximities)); if (!isGeometric && pointerId == 0) { - // Initialize - // - mInputCodes - // - mNormalizedSquaredDistances - // TODO: Merge - for (int i = 0; i < inputSize; ++i) { - const int primaryKey = inputCodes[i]; - const int x = xCoordinates[i]; - const int y = yCoordinates[i]; - int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL]; - mProximityInfo->calculateNearbyKeyCodes(x, y, primaryKey, proximities); - } - - if (DEBUG_PROXIMITY_CHARS) { - for (int i = 0; i < inputSize; ++i) { - AKLOGI("---"); - for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) { - int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; - int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; - icc += 0; - icfjc += 0; - AKLOGI("--- (%d)%c,%c", i, icc, icfjc); AKLOGI("--- A<%d>,B<%d>", icc, icfjc); - } - } - } + mProximityInfo->initializeProximities(inputCodes, xCoordinates, yCoordinates, + inputSize, mInputProximities); } /////////////////////// diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index d747bae2a..bc2cf505c 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -61,7 +61,7 @@ class ProximityInfoState { mInputIndice(), mLengthCache(), mBeelineSpeedPercentiles(), mDistanceCache_G(), mSpeedRates(), mDirections(), mCharProbabilities(), mNearKeysVector(), mSearchKeysVector(), mTouchPositionCorrectionEnabled(false), mSampledInputSize(0) { - memset(mInputCodes, 0, sizeof(mInputCodes)); + memset(mInputProximities, 0, sizeof(mInputProximities)); memset(mNormalizedSquaredDistances, 0, sizeof(mNormalizedSquaredDistances)); memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord)); } @@ -117,12 +117,12 @@ class ProximityInfoState { if (length != mSampledInputSize) { return false; } - const int *inputCodes = mInputCodes; + const int *inputProximities = mInputProximities; while (length--) { - if (*inputCodes != *word) { + if (*inputProximities != *word) { return false; } - inputCodes += MAX_PROXIMITY_CHARS_SIZE_INTERNAL; + inputProximities += MAX_PROXIMITY_CHARS_SIZE_INTERNAL; word++; } return true; @@ -229,7 +229,7 @@ class ProximityInfoState { } inline const int *getProximityCodePointsAt(const int index) const { - return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL); + return mInputProximities + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL); } float updateNearKeysDistances(const int x, const int y, @@ -289,7 +289,7 @@ class ProximityInfoState { // inputs including the current input point. std::vector<NearKeycodesSet> mSearchKeysVector; bool mTouchPositionCorrectionEnabled; - int mInputCodes[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH]; + int mInputProximities[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH]; int mNormalizedSquaredDistances[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH]; int mSampledInputSize; int mPrimaryInputWord[MAX_WORD_LENGTH]; diff --git a/native/jni/src/proximity_info_utils.h b/native/jni/src/proximity_info_utils.h new file mode 100644 index 000000000..b9348d885 --- /dev/null +++ b/native/jni/src/proximity_info_utils.h @@ -0,0 +1,192 @@ +/* + * 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. + */ + +#ifndef LATINIME_PROXIMITY_INFO_UTILS_H +#define LATINIME_PROXIMITY_INFO_UTILS_H + +#include "additional_proximity_chars.h" +#include "char_utils.h" +#include "defines.h" +#include "hash_map_compat.h" + +namespace latinime { +class ProximityInfoUtils { + public: + static int getKeyIndexOf(const int keyCount, const int c, + const hash_map_compat<int, int> *const codeToKeyMap) { + if (keyCount == 0) { + // We do not have the coordinate data + return NOT_AN_INDEX; + } + if (c == NOT_A_CODE_POINT) { + return NOT_AN_INDEX; + } + const int lowerCode = toLowerCase(c); + hash_map_compat<int, int>::const_iterator mapPos = codeToKeyMap->find(lowerCode); + if (mapPos != codeToKeyMap->end()) { + return mapPos->second; + } + return NOT_AN_INDEX; + } + + static void initializeProximities(const int *const inputCodes, + const int *const inputXCoordinates, const int *const inputYCoordinates, + const int inputSize, const int *const keyXCoordinates, + const int *const keyYCoordinates, const int *const keyWidths, const int *keyHeights, + const int *const proximityCharsArray, const int maxProximityCharsSize, + const int cellHeight, const int cellWidth, const int gridWidth, + const int mostCommonKeyWidth, const int keyCount, const char *const localeStr, + const hash_map_compat<int, int> *const codeToKeyMap, int *inputProximities) { + // Initialize + // - mInputCodes + // - mNormalizedSquaredDistances + // TODO: Merge + for (int i = 0; i < inputSize; ++i) { + const int primaryKey = inputCodes[i]; + const int x = inputXCoordinates[i]; + const int y = inputYCoordinates[i]; + int *proximities = &inputProximities[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL]; + calculateProximities(keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, + proximityCharsArray, maxProximityCharsSize, cellHeight, cellWidth, gridWidth, + mostCommonKeyWidth, keyCount, x, y, primaryKey, localeStr, codeToKeyMap, + proximities); + } + + if (DEBUG_PROXIMITY_CHARS) { + for (int i = 0; i < inputSize; ++i) { + AKLOGI("---"); + for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) { + int proximityChar = + inputProximities[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; + proximityChar += 0; + AKLOGI("--- (%d)%c", i, proximityChar); + } + } + } + } + + AK_FORCE_INLINE static int getStartIndexFromCoordinates(const int maxProximityCharsSize, + const int x, const int y, const int cellHeight, const int cellWidth, + const int gridWidth) { + return ((y / cellHeight) * gridWidth + (x / cellWidth)) * maxProximityCharsSize; + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfoUtils); + + static bool isOnKey(const int *const keyXCoordinates, const int *const keyYCoordinates, + const int *const keyWidths, const int *keyHeights, const int keyId, const int x, + const int y) { + if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case + const int left = keyXCoordinates[keyId]; + const int top = keyYCoordinates[keyId]; + const int right = left + keyWidths[keyId] + 1; + const int bottom = top + keyHeights[keyId]; + return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom; + } + + static void calculateProximities( + const int *const keyXCoordinates, const int *const keyYCoordinates, + const int *const keyWidths, const int *keyHeights, + const int *const proximityCharsArray, + const int maxProximityCharsSize, const int cellHeight, const int cellWidth, + const int gridWidth, const int mostCommonKeyWidth, const int keyCount, + const int x, const int y, const int primaryKey, const char *const localeStr, + const hash_map_compat<int, int> *const codeToKeyMap, int *proximities) { + const int mostCommonKeyWidthSquare = mostCommonKeyWidth * mostCommonKeyWidth; + int insertPos = 0; + proximities[insertPos++] = primaryKey; + const int startIndex = getStartIndexFromCoordinates( + maxProximityCharsSize, x, y, cellHeight, cellWidth, gridWidth); + if (startIndex >= 0) { + for (int i = 0; i < maxProximityCharsSize; ++i) { + const int c = proximityCharsArray[startIndex + i]; + if (c < KEYCODE_SPACE || c == primaryKey) { + continue; + } + const int keyIndex = getKeyIndexOf(keyCount, c, codeToKeyMap); + const bool onKey = isOnKey(keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, + keyIndex, x, y); + const int distance = squaredLengthToEdge(keyXCoordinates, keyYCoordinates, + keyWidths, keyHeights, keyIndex, x, y); + if (onKey || distance < mostCommonKeyWidthSquare) { + proximities[insertPos++] = c; + if (insertPos >= maxProximityCharsSize) { + if (DEBUG_DICT) { + ASSERT(false); + } + return; + } + } + } + const int additionalProximitySize = + AdditionalProximityChars::getAdditionalCharsSize(localeStr, primaryKey); + if (additionalProximitySize > 0) { + proximities[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE; + if (insertPos >= maxProximityCharsSize) { + if (DEBUG_DICT) { + ASSERT(false); + } + return; + } + + const int *additionalProximityChars = + AdditionalProximityChars::getAdditionalChars(localeStr, primaryKey); + for (int j = 0; j < additionalProximitySize; ++j) { + const int ac = additionalProximityChars[j]; + int k = 0; + for (; k < insertPos; ++k) { + if (ac == proximities[k]) { + break; + } + } + if (k < insertPos) { + continue; + } + proximities[insertPos++] = ac; + if (insertPos >= maxProximityCharsSize) { + if (DEBUG_DICT) { + ASSERT(false); + } + return; + } + } + } + } + // Add a delimiter for the proximity characters + for (int i = insertPos; i < maxProximityCharsSize; ++i) { + proximities[i] = NOT_A_CODE_POINT; + } + } + + static int squaredLengthToEdge(const int *const keyXCoordinates, + const int *const keyYCoordinates, const int *const keyWidths, const int *keyHeights, + const int keyId, const int x, const int y) { + // NOT_A_ID is -1, but return whenever < 0 just in case + if (keyId < 0) return MAX_POINT_TO_KEY_LENGTH; + const int left = keyXCoordinates[keyId]; + const int top = keyYCoordinates[keyId]; + const int right = left + keyWidths[keyId]; + const int bottom = top + keyHeights[keyId]; + const int edgeX = x < left ? left : (x > right ? right : x); + const int edgeY = y < top ? top : (y > bottom ? bottom : y); + const int dx = x - edgeX; + const int dy = y - edgeY; + return dx * dx + dy * dy; + } +}; +} // namespace latinime +#endif // LATINIME_PROXIMITY_INFO_UTILS_H |