[AC4] Add native methods necessary for auto-commit

Bug: 9059617
Change-Id: I7a47b0675446fc4f39628c60d16de9aea90d1b4d
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index e8b0657..eb5c4be 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -40,6 +40,9 @@
     private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
     // Must be equal to MAX_RESULTS in native/jni/src/defines.h
     private static final int MAX_RESULTS = 18;
+    // Required space count for auto commit.
+    // TODO: Remove this heuristic.
+    private static final int SPACE_COUNT_FOR_AUTO_COMMIT = 3;
 
     private long mNativeDict;
     private final Locale mLocale;
@@ -48,6 +51,7 @@
     private final int[] mSpaceIndices = new int[MAX_RESULTS];
     private final int[] mOutputScores = new int[MAX_RESULTS];
     private final int[] mOutputTypes = new int[MAX_RESULTS];
+    private final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; // Only one result
 
     private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
 
@@ -102,7 +106,8 @@
             long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
             int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
             int[] suggestOptions, int[] prevWordCodePointArray,
-            int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes);
+            int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes,
+            int[] outputAutoCommitFirstWordConfidence);
     private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
     private static native int editDistanceNative(int[] before, int[] after);
     private static native void addUnigramWordNative(long dict, int[] word, int probability);
@@ -155,7 +160,7 @@
                 ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints,
                 inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(),
                 prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices,
-                mOutputTypes);
+                mOutputTypes, mOutputAutoCommitFirstWordConfidence);
         final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
         for (int j = 0; j < count; ++j) {
             final int start = j * MAX_WORD_LENGTH;
@@ -179,7 +184,8 @@
                 // flags too and pass mOutputTypes[j] instead of kind
                 suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
                         score, kind, this /* sourceDict */,
-                        mSpaceIndices[0] /* indexOfTouchPointOfSecondWord */));
+                        mSpaceIndices[0] /* indexOfTouchPointOfSecondWord */,
+                        mOutputAutoCommitFirstWordConfidence[0]));
             }
         }
         return suggestions;
@@ -254,6 +260,22 @@
     }
 
     @Override
+    public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
+        // TODO: actually use the confidence rather than use this completely broken heuristic
+        final String word = candidate.mWord;
+        final int length = word.length();
+        int remainingSpaces = SPACE_COUNT_FOR_AUTO_COMMIT;
+        for (int i = 0; i < length; ++i) {
+            // This is okay because no low-surrogate and no high-surrogate can ever match the
+            // space character, so we don't need to take care of iterating on code points.
+            if (Constants.CODE_SPACE == word.charAt(i)) {
+                if (0 >= --remainingSpaces) return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
     public void close() {
         synchronized (mDicTraverseSessions) {
             final int sessionsSize = mDicTraverseSessions.size();
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 8a3a884..fa79f5a 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -137,7 +137,10 @@
     }
 
     /**
-     * Whether we think this suggestion should trigger an auto-commit.
+     * Whether we think this suggestion should trigger an auto-commit. prevWord is the word
+     * before the suggestion, so that we can use n-gram frequencies.
+     * @param candidate The candidate suggestion, in whole (not only the first part).
+     * @return whether we should auto-commit or not.
      */
     public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
         // If we don't have support for auto-commit, or if we don't know, we return false to
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index ba7d1a2..d491f98 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -344,7 +344,8 @@
             // in the future.
             suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq,
                     SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */,
-                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
             if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false;
         }
         if (null != node.mShortcutTargets) {
@@ -353,7 +354,8 @@
                 final char[] shortcut = node.mShortcutTargets.get(shortcutIndex);
                 suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length),
                         finalFreq, SuggestedWordInfo.KIND_SHORTCUT, this /* sourceDict */,
-                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                        SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
                 if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false;
             }
         }
@@ -604,7 +606,8 @@
                 suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index,
                         Constants.DICTIONARY_MAX_WORD_LENGTH - index),
                         freq, SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */,
-                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                        SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a325c40..b7b86b7 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -2697,7 +2697,9 @@
                     suggestions.add(new SuggestedWordInfo(s,
                             SuggestionStripView.MAX_SUGGESTIONS - i,
                             SuggestedWordInfo.KIND_RESUMED, Dictionary.DICTIONARY_RESUMED,
-                            SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                            SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                            SuggestedWordInfo.NOT_A_CONFIDENCE
+                                    /* autoCommitFirstWordConfidence */));
                 }
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9370757..7815f4d 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -327,7 +327,8 @@
             suggestionsContainer.add(0, new SuggestedWordInfo(typedWord,
                     SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED,
                     Dictionary.DICTIONARY_USER_TYPED,
-                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         }
         SuggestedWordInfo.removeDups(suggestionsContainer);
 
@@ -474,7 +475,8 @@
             sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE);
         }
         return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind,
-                wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord);
+                wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
     }
 
     public void close() {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index b27fd81..1763705 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -114,7 +114,8 @@
             final SuggestedWordInfo suggestedWordInfo = new SuggestedWordInfo(text.toString(),
                     SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_APP_DEFINED,
                     Dictionary.DICTIONARY_APPLICATION_DEFINED,
-                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */);
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
             result.add(suggestedWordInfo);
         }
         return result;
@@ -128,7 +129,8 @@
         final HashSet<String> alreadySeen = CollectionUtils.newHashSet();
         suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
                 SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
-                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         alreadySeen.add(typedWord.toString());
         final int previousSize = previousSuggestions.size();
         for (int index = 1; index < previousSize; index++) {
@@ -151,6 +153,7 @@
 
     public static final class SuggestedWordInfo {
         public static final int NOT_AN_INDEX = -1;
+        public static final int NOT_A_CONFIDENCE = -1;
         public static final int MAX_SCORE = Integer.MAX_VALUE;
         public static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
         public static final int KIND_TYPED = 0; // What user typed
@@ -180,16 +183,30 @@
         // passed to native code to get suggestions for a gesture that corresponds to the first
         // letter of the second word.
         public final int mIndexOfTouchPointOfSecondWord;
+        // For auto-commit. This is a measure of how confident we are that we can commit the
+        // first word of this suggestion.
+        public final int mAutoCommitFirstWordConfidence;
         private String mDebugString = "";
 
+        /**
+         * Create a new suggested word info.
+         * @param word The string to suggest.
+         * @param score A measure of how likely this suggestion is.
+         * @param kind The kind of suggestion, as one of the above KIND_* constants.
+         * @param sourceDict What instance of Dictionary produced this suggestion.
+         * @param indexOfTouchPointOfSecondWord See mIndexOfTouchPointOfSecondWord.
+         * @param autoCommitFirstWordConfidence See mAutoCommitFirstWordConfidence.
+         */
         public SuggestedWordInfo(final String word, final int score, final int kind,
-                final Dictionary sourceDict, final int indexOfTouchPointOfSecondWord) {
+                final Dictionary sourceDict, final int indexOfTouchPointOfSecondWord,
+                final int autoCommitFirstWordConfidence) {
             mWord = word;
             mScore = score;
             mKind = kind;
             mSourceDict = sourceDict;
             mCodePointCount = StringUtils.codePointCount(mWord);
             mIndexOfTouchPointOfSecondWord = indexOfTouchPointOfSecondWord;
+            mAutoCommitFirstWordConfidence = autoCommitFirstWordConfidence;
         }
 
         public boolean isEligibleForAutoCommit() {
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 072bb87..fc2d192 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -295,7 +295,8 @@
                 puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec),
                         SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED,
                         Dictionary.DICTIONARY_HARDCODED,
-                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                        SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
             }
         }
         return new SuggestedWords(puncList,
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 8da1859..6a36612 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -70,7 +70,8 @@
         jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray,
         jintArray inputCodePointsArray, jint inputSize, jint commitPoint, jintArray suggestOptions,
         jintArray prevWordCodePointsForBigrams, jintArray outputCodePointsArray,
-        jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray) {
+        jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray,
+        jintArray outputAutoCommitFirstWordConfidence) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
     if (!dictionary) return 0;
     ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo);
@@ -253,7 +254,7 @@
     },
     {
         const_cast<char *>("getSuggestionsNative"),
-        const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I)I"),
+        const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I)I"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)
     },
     {
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 2603b35..234bb1b 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -259,7 +259,8 @@
     protected void pickSuggestionManually(final int index, final String suggestion) {
         mLatinIME.pickSuggestionManually(index, new SuggestedWordInfo(suggestion, 1,
                 SuggestedWordInfo.KIND_CORRECTION, null /* sourceDict */,
-                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
     }
 
     // Helper to avoid writing the try{}catch block each time
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
index a5f3685..4cf8333 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -35,11 +35,13 @@
         final ArrayList<SuggestedWordInfo> list = CollectionUtils.newArrayList();
         list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ,
                 SuggestedWordInfo.KIND_TYPED, null /* sourceDict */,
-                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) {
             list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION,
                     null /* sourceDict */,
-                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */));
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         }
 
         final SuggestedWords words = new SuggestedWords(