Merge "Don't reset animation alpha value when keyboard is set"
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index e111bdd..aec1703 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -37,12 +37,9 @@
     <string name="misc_category" msgid="6894192814868233453">"Otras opciones"</string>
     <string name="advanced_settings" msgid="362895144495591463">"Configuración avanzada"</string>
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opciones para expertos"</string>
-    <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
-    <skip />
-    <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
-    <skip />
-    <!-- no translation found for suppress_language_switch_key (8003788410354806368) -->
-    <skip />
+    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Otros métodos de entrada"</string>
+    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de cambio de idioma abarca otros métodos de entrada."</string>
+    <string name="suppress_language_switch_key" msgid="8003788410354806368">"Supr. tecla cambio idioma"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso en rechazo de alerta de tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin demora"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 2018c55..3dd1519 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -37,12 +37,9 @@
     <string name="misc_category" msgid="6894192814868233453">"Otras opciones"</string>
     <string name="advanced_settings" msgid="362895144495591463">"Ajustes avanzados"</string>
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opciones para expertos"</string>
-    <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
-    <skip />
-    <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
-    <skip />
-    <!-- no translation found for suppress_language_switch_key (8003788410354806368) -->
-    <skip />
+    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Otros métodos de introducción"</string>
+    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de cambio de idioma sirve también para otros métodos."</string>
+    <string name="suppress_language_switch_key" msgid="8003788410354806368">"Eliminar tecla cambiar idioma"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso de rechazo"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin retraso"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index d65a568..a3f01e1 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -37,12 +37,9 @@
     <string name="misc_category" msgid="6894192814868233453">"Altre opzioni"</string>
     <string name="advanced_settings" msgid="362895144495591463">"Impostazioni avanzate"</string>
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opzioni per esperti"</string>
-    <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
-    <skip />
-    <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
-    <skip />
-    <!-- no translation found for suppress_language_switch_key (8003788410354806368) -->
-    <skip />
+    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Altri metodi immissione"</string>
+    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Il tasto per cambiare lingua offre altri metodi di immissione"</string>
+    <string name="suppress_language_switch_key" msgid="8003788410354806368">"Elimina tasto cambio lingua"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ritardo eliminaz. popup tasto"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nessun ritardo"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</string>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 3638eae..ea3f623 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -97,21 +97,21 @@
 
     /**
      * Computes maximum size of the array that can contain all nearby key codes returned by
-     * {@link #getKeyAndNearbyCodes}.
+     * {@link #getNearbyCodes}.
      *
      * @return Returns maximum size of the array that can contain all nearby key codes returned
-     *         by {@link #getKeyAndNearbyCodes}.
+     *         by {@link #getNearbyCodes}.
      */
     protected int getMaxNearbyKeys() {
         return MAX_NEARBY_KEYS;
     }
 
     /**
-     * Allocates array that can hold all key codes returned by {@link #getKeyAndNearbyCodes}
+     * Allocates array that can hold all key codes returned by {@link #getNearbyCodes}
      * method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}.
      *
      * @return Allocates and returns an array that can hold all key codes returned by
-     *         {@link #getKeyAndNearbyCodes} method. All elements in the returned array are
+     *         {@link #getNearbyCodes} method. All elements in the returned array are
      *         initialized by {@link #NOT_A_CODE} value.
      */
     public int[] newCodeArray() {
@@ -222,15 +222,15 @@
      * @param x The x-coordinate of a touch point
      * @param y The y-coordinate of a touch point
      * @param allCodes All nearby key codes except functional key are returned in this array
-     * @return The nearest key
      */
-    public Key getKeyAndNearbyCodes(int x, int y, final int[] allCodes) {
+    // TODO: Move this method to native code.
+    public void getNearbyCodes(int x, int y, final int[] allCodes) {
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
         initializeNearbyKeys();
         Key primaryKey = null;
-        for (final Key key: mKeyboard.getNearestKeys(touchX, touchY)) {
+        for (final Key key : mKeyboard.getNearestKeys(touchX, touchY)) {
             final boolean isOnKey = key.isOnKey(touchX, touchY);
             final int distance = key.squaredDistanceToEdge(touchX, touchY);
             if (isOnKey || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
@@ -241,16 +241,31 @@
             }
         }
 
-        if (allCodes != null && allCodes.length > 0) {
-            getNearbyKeyCodes(primaryKey != null ? primaryKey.mCode : NOT_A_CODE, allCodes);
-            if (DEBUG) {
-                Log.d(TAG, "x=" + x + " y=" + y
-                        + " primary=" + printableCode(primaryKey)
-                        + " codes=" + printableCodes(allCodes));
+        getNearbyKeyCodes(primaryKey != null ? primaryKey.mCode : NOT_A_CODE, allCodes);
+        if (DEBUG) {
+            Log.d(TAG, "x=" + x + " y=" + y
+                    + " primary=" + printableCode(primaryKey)
+                    + " codes=" + printableCodes(allCodes));
+        }
+    }
+
+    /**
+     * Detect the key whose hitbox the touch point is in.
+     *
+     * @param x The x-coordinate of a touch point
+     * @param y The y-coordinate of a touch point
+     * @return the key that the touch point hits.
+     */
+    public Key detectHitKey(int x, int y) {
+        final int touchX = getTouchX(x);
+        final int touchY = getTouchY(y);
+
+        for (final Key key : mKeyboard.getNearestKeys(touchX, touchY)) {
+            if (key.isOnKey(touchX, touchY)) {
+                return key;
             }
         }
-
-        return primaryKey;
+        return null;
     }
 
     public static String printableCode(Key key) {
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index 742ee98..6c8d020 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -39,7 +39,7 @@
     }
 
     @Override
-    public Key getKeyAndNearbyCodes(int x, int y, final int[] allCodes) {
+    public void getNearbyCodes(int x, int y, final int[] allCodes) {
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
@@ -53,8 +53,26 @@
             }
         }
 
-        if (allCodes != null && nearestKey != null) {
+        if (nearestKey != null) {
             allCodes[0] = nearestKey.mCode;
+        } else {
+            allCodes[0] = NOT_A_CODE;
+        }
+    }
+
+    @Override
+    public Key detectHitKey(int x, int y) {
+        final int touchX = getTouchX(x);
+        final int touchY = getTouchY(y);
+
+        Key nearestKey = null;
+        int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
+        for (final Key key : getKeyboard().mKeys) {
+            final int dist = key.squaredDistanceToEdge(touchX, touchY);
+            if (dist < nearestDist) {
+                nearestKey = key;
+                nearestDist = dist;
+            }
         }
         return nearestKey;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index ed88971..ec90816 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -318,7 +318,7 @@
     }
 
     public Key getKeyOn(int x, int y) {
-        return mKeyDetector.getKeyAndNearbyCodes(x, y, null);
+        return mKeyDetector.detectHitKey(x, y);
     }
 
     private void setReleasedKeyGraphics(Key key) {
@@ -421,7 +421,7 @@
     private Key onMoveKeyInternal(int x, int y) {
         mLastX = x;
         mLastY = y;
-        return mKeyDetector.getKeyAndNearbyCodes(x, y, null);
+        return mKeyDetector.detectHitKey(x, y);
     }
 
     private Key onMoveKey(int x, int y) {
@@ -748,7 +748,7 @@
     private long mPreviousEventTime;
 
     private void printTouchEvent(String title, int x, int y, long eventTime) {
-        final Key key = mKeyDetector.getKeyAndNearbyCodes(x, y, null);
+        final Key key = mKeyDetector.detectHitKey(x, y);
         final String code = KeyDetector.printableCode(key);
         final long delta = eventTime - mPreviousEventTime;
         Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index c35c960..7c08377 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -459,6 +459,8 @@
 
         // TODO: remove the following when it's not needed by updateCorrectionMode() any more
         mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
+        updateCorrectionMode();
+
         Utils.GCUtils.getInstance().reset();
         boolean tryGC = true;
         for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
@@ -534,8 +536,6 @@
                 = new UserBigramDictionary(this, this, localeStr, Suggest.DIC_USER_BIGRAM);
         mSuggest.setUserBigramDictionary(mUserBigramDictionary);
 
-        updateCorrectionMode();
-
         LocaleUtils.setSystemLocale(res, savedLocale);
     }
 
@@ -1114,8 +1114,7 @@
             if (ic != null) {
                 ic.commitText(typedWord, 1);
             }
-            addToUserUnigramAndBigramDictionaries(typedWord,
-                    UserUnigramDictionary.FREQUENCY_FOR_TYPED);
+            addToUserHistoryDictionary(typedWord);
         }
         updateSuggestions();
     }
@@ -1834,9 +1833,8 @@
             mExpectingUpdateSelection = true;
             commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
                     separatorCodePoint);
-            // Add the word to the user unigram dictionary if it's not a known word
-            addToUserUnigramAndBigramDictionaries(autoCorrection,
-                    UserUnigramDictionary.FREQUENCY_FOR_TYPED);
+            // Add the word to the user history dictionary
+            addToUserHistoryDictionary(autoCorrection);
             if (!typedWord.equals(autoCorrection) && null != ic) {
                 // This will make the correction flash for a short while as a visual clue
                 // to the user that auto-correction happened.
@@ -1895,13 +1893,8 @@
         mExpectingUpdateSelection = true;
         commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
                 LastComposedWord.NOT_A_SEPARATOR);
-        // Add the word to the auto dictionary if it's not a known word
-        if (index == 0) {
-            addToUserUnigramAndBigramDictionaries(suggestion,
-                    UserUnigramDictionary.FREQUENCY_FOR_PICKED);
-        } else {
-            addToOnlyBigramDictionary(suggestion, 1);
-        }
+        // Add the word to the user history dictionary
+        addToUserHistoryDictionary(suggestion);
         mSpaceState = SPACE_STATE_PHANTOM;
         // TODO: is this necessary?
         mKeyboardSwitcher.updateShiftState();
@@ -2002,21 +1995,10 @@
         setSuggestionStripShown(isSuggestionsStripVisible());
     }
 
-    private void addToUserUnigramAndBigramDictionaries(CharSequence suggestion,
-            int frequencyDelta) {
-        checkAddToDictionary(suggestion, frequencyDelta, false);
-    }
-
-    private void addToOnlyBigramDictionary(CharSequence suggestion, int frequencyDelta) {
-        checkAddToDictionary(suggestion, frequencyDelta, true);
-    }
-
     /**
      * Adds to the UserBigramDictionary and/or UserUnigramDictionary
-     * @param selectedANotTypedWord true if it should be added to bigram dictionary if possible
      */
-    private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta,
-            boolean selectedANotTypedWord) {
+    private void addToUserHistoryDictionary(final CharSequence suggestion) {
         if (suggestion == null || suggestion.length() < 1) return;
 
         // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
@@ -2027,28 +2009,17 @@
             return;
         }
 
-        if (null != mSuggest && null != mUserUnigramDictionary) {
-            final boolean selectedATypedWordAndItsInUserUnigramDic =
-                    !selectedANotTypedWord && mUserUnigramDictionary.isValidWord(suggestion);
-            final boolean isValidWord = AutoCorrection.isValidWord(
-                    mSuggest.getUnigramDictionaries(), suggestion, true);
-            final boolean needsToAddToUserUnigramDictionary =
-                    selectedATypedWordAndItsInUserUnigramDic || !isValidWord;
-            if (needsToAddToUserUnigramDictionary) {
-                mUserUnigramDictionary.addWord(suggestion.toString(), frequencyDelta);
-            }
+        if (null != mUserUnigramDictionary) {
+            mUserUnigramDictionary.addUnigram(suggestion.toString());
         }
 
         if (mUserBigramDictionary != null) {
-            // We don't want to register as bigrams words separated by a separator.
-            // For example "I will, and you too" : we don't want the pair ("will" "and") to be
-            // a bigram.
             final InputConnection ic = getCurrentInputConnection();
             if (null != ic) {
                 final CharSequence prevWord =
                         EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
-                if (!TextUtils.isEmpty(prevWord)) {
-                    mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
+                if (null != prevWord) {
+                    mUserBigramDictionary.addBigramPair(prevWord.toString(), suggestion.toString());
                 }
             }
         }
@@ -2059,10 +2030,12 @@
         if (ic == null) return false;
         CharSequence before = ic.getTextBeforeCursor(1, 0);
         CharSequence after = ic.getTextAfterCursor(1, 0);
-        if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0))) {
+        if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0))
+                && !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
             return true;
         }
-        if (!TextUtils.isEmpty(after) && !mSettingsValues.isWordSeparator(after.charAt(0))) {
+        if (!TextUtils.isEmpty(after) && !mSettingsValues.isWordSeparator(after.charAt(0))
+                && !mSettingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) {
             return true;
         }
         return false;
@@ -2233,6 +2206,7 @@
             mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
         }
         initSuggest();
+        updateCorrectionMode();
         loadSettings();
         // Since we just changed languages, we should re-evaluate suggestions with whatever word
         // we are currently composing. If we are not composing anything, we may want to display
diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
index e6a59d0..52a31f2 100644
--- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
@@ -157,7 +157,7 @@
     /**
      * Pair will be added to the userbigram database.
      */
-    public int addBigrams(String word1, String word2) {
+    public int addBigramPair(String word1, String word2) {
         // remove caps if second word is autocapitalized
         if (mIme != null && mIme.isAutoCapitalized()) {
             word2 = Character.toLowerCase(word2.charAt(0)) + word2.substring(1);
diff --git a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
index 869865d..2fc395c 100644
--- a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
@@ -143,9 +143,9 @@
         }
     }
 
-    @Override
-    public void addWord(String newWord, int addFrequency) {
+    public void addUnigram(String newWord) {
         if (!ENABLE_USER_UNIGRAM_DICTIONARY) return;
+        final int addFrequency = FREQUENCY_FOR_TYPED;
         String word = newWord;
         final int length = word.length();
         // Don't add very short or very long words.
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 5f6be68..a960931 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -141,7 +141,7 @@
             keyY = y;
         } else {
             codes = keyDetector.newCodeArray();
-            keyDetector.getKeyAndNearbyCodes(x, y, codes);
+            keyDetector.getNearbyCodes(x, y, codes);
             keyX = keyDetector.getTouchX(x);
             keyY = keyDetector.getTouchY(y);
         }
@@ -177,7 +177,6 @@
     private void add(int primaryCode, int[] codes, int keyX, int keyY) {
         final int newIndex = mCodes.size();
         mTypedWord.appendCodePoint(primaryCode);
-        correctPrimaryJuxtapos(primaryCode, codes);
         mCodes.add(codes);
         if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
             mXCoordinates[newIndex] = keyX;
@@ -204,7 +203,7 @@
                 final int x = key.mX + key.mWidth / 2;
                 final int y = key.mY + key.mHeight / 2;
                 final int[] codes = keyDetector.newCodeArray();
-                keyDetector.getKeyAndNearbyCodes(x, y, codes);
+                keyDetector.getNearbyCodes(x, y, codes);
                 add(codePoint, codes, x, y);
                 return;
             }
@@ -239,21 +238,6 @@
     }
 
     /**
-     * Swaps the first and second values in the codes array if the primary code is not the first
-     * value in the array but the second. This happens when the preferred key is not the key that
-     * the user released the finger on.
-     * @param primaryCode the preferred character
-     * @param codes array of codes based on distance from touch point
-     */
-    private static void correctPrimaryJuxtapos(int primaryCode, int[] codes) {
-        if (codes.length < 2) return;
-        if (codes[0] > 0 && codes[1] > 0 && codes[0] != primaryCode && codes[1] == primaryCode) {
-            codes[1] = codes[0];
-            codes[0] = primaryCode;
-        }
-    }
-
-    /**
      * Delete the last keystroke as a result of hitting backspace.
      */
     public void deleteLast() {
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index a6a4d93..595fe5b 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -554,6 +554,22 @@
                 EXPECTED_RESULT, mTextView.getText().toString());
     }
 
+    public void testAutoCorrectionWithSingleQuoteInside() {
+        final String WORD_TO_TYPE = "you'f ";
+        final String EXPECTED_RESULT = "you'd ";
+        type(WORD_TO_TYPE);
+        assertEquals("auto-correction with single quote inside",
+                EXPECTED_RESULT, mTextView.getText().toString());
+    }
+
+    public void testAutoCorrectionWithSingleQuotesAround() {
+        final String WORD_TO_TYPE = "'tgis' ";
+        final String EXPECTED_RESULT = "'this' ";
+        type(WORD_TO_TYPE);
+        assertEquals("auto-correction with single quotes around",
+                EXPECTED_RESULT, mTextView.getText().toString());
+    }
+
     // A helper class to ease span tests
     private static class Span {
         final SpannableStringBuilder mInputText;