Merge "Fix a long-standing race condition."
diff --git a/dictionaries/sample.xml b/dictionaries/sample.xml
index 85233b6..ad98f2b 100644
--- a/dictionaries/sample.xml
+++ b/dictionaries/sample.xml
@@ -2,7 +2,9 @@
      for use by the Latin IME.
      The format of the word list is a flat list of word entries.
      Each entry has a frequency between 255 and 0.
-     Highest frequency words get more weight in the prediction algorithm.
+     Highest frequency words get more weight in the prediction algorithm. As a
+     special case, a weight of 0 is taken to mean profanity - words that should
+     not be considered a typo, but that should never be suggested explicitly.
      You can capitalize words that must always be capitalized, such as "January".
      You can have a capitalized and a non-capitalized word as separate entries,
      such as "robin" and "Robin".
@@ -13,4 +15,3 @@
   <w f="128">sample</w>
   <w f="1">wordlist</w>
 </wordlist>
-
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 23abd67..34699a4 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1510,13 +1510,16 @@
             if (null != ic) removeTrailingSpaceWhileInBatchEdit(ic);
         }
 
+        boolean isComposingWord = mHasUncommittedTypedChars;
         int code = primaryCode;
         if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code))
                 && isSuggestionsRequested() && !isCursorTouchingWord()) {
-            if (!mHasUncommittedTypedChars) {
+            if (!isComposingWord) {
                 // Reset entirely the composing state anyway, then start composing a new word unless
-                // the character is a single quote.
-                mHasUncommittedTypedChars = (Keyboard.CODE_SINGLE_QUOTE != code);
+                // the character is a single quote. The idea here is, single quote is not a
+                // separator and it should be treated as a normal character, except in the first
+                // position where it should not start composing a word.
+                isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != code);
                 mWordComposer.reset();
                 clearSuggestions();
                 mComposingStateManager.onFinishComposingText();
@@ -1543,7 +1546,8 @@
                 }
             }
         }
-        if (mHasUncommittedTypedChars) {
+        if (isComposingWord) {
+            mHasUncommittedTypedChars = true;
             mWordComposer.add(code, keyCodes, x, y);
             if (ic != null) {
                 // If it's the first letter, make note of auto-caps state
@@ -1785,7 +1789,6 @@
             return;
         }
 
-        final WordComposer wordComposer = mWordComposer;
         // TODO: May need a better way of retrieving previous word
         final InputConnection ic = getCurrentInputConnection();
         final CharSequence prevWord;
@@ -1795,18 +1798,18 @@
             prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
         }
         // getSuggestedWordBuilder handles gracefully a null value of prevWord
-        final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(wordComposer,
+        final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(mWordComposer,
                 prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode);
 
         boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
-        final CharSequence typedWord = wordComposer.getTypedWord();
+        final CharSequence typedWord = mWordComposer.getTypedWord();
         // Here, we want to promote a whitelisted word if exists.
         // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
         // but still autocorrected from - in the case the whitelist only capitalizes the word.
         // The whitelist should be case-insensitive, so it's not possible to be consistent with
         // a boolean flag. Right now this is handled with a slight hack in
         // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
-        final int quotesCount = wordComposer.trailingSingleQuotesCount();
+        final int quotesCount = mWordComposer.trailingSingleQuotesCount();
         final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected(
                 mSuggest.getUnigramDictionaries(),
                 // If the typed string ends with a single quote, for dictionary lookup purposes
@@ -1822,7 +1825,7 @@
             autoCorrectionAvailable |= (!allowsToBeAutoCorrected);
         }
         // Don't auto-correct words with multiple capital letter
-        autoCorrectionAvailable &= !wordComposer.isMostlyCaps();
+        autoCorrectionAvailable &= !mWordComposer.isMostlyCaps();
 
         // Basically, we update the suggestion strip only when suggestion count > 1.  However,
         // there is an exception: We update the suggestion strip whenever typed word's length
@@ -1959,6 +1962,8 @@
         } else {
             addToOnlyBigramDictionary(suggestion, 1);
         }
+        // TODO: the following is fishy, because if !mHasUncommittedTypedChars we are
+        // going to log an empty string
         LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(),
                 suggestion.toString(), index, suggestions.mWords);
         // Follow it with a space
@@ -2399,8 +2404,8 @@
         }
     }
 
-    public WordComposer getCurrentWord() {
-        return mWordComposer;
+    public boolean isAutoCapitalized() {
+        return mWordComposer.isAutoCapitalized();
     }
 
     boolean isSoundOn() {
diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
index 3a1af93..f80534c 100644
--- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
@@ -159,7 +159,7 @@
      */
     public int addBigrams(String word1, String word2) {
         // remove caps if second word is autocapitalized
-        if (mIme != null && mIme.getCurrentWord().isAutoCapitalized()) {
+        if (mIme != null && mIme.isAutoCapitalized()) {
             word2 = Character.toLowerCase(word2.charAt(0)) + word2.substring(1);
         }
         // Do not insert a word as a bigram of itself
diff --git a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
index de7cb57..6af20c7 100644
--- a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
@@ -149,7 +149,7 @@
         final int length = word.length();
         // Don't add very short or very long words.
         if (length < 2 || length > getMaxWordLength()) return;
-        if (mIme.getCurrentWord().isAutoCapitalized()) {
+        if (mIme.isAutoCapitalized()) {
             // Remove caps before adding
             word = Character.toLowerCase(word.charAt(0)) + word.substring(1);
         }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index fcaf81c..8bbcff7 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -301,7 +301,7 @@
     }
 
     /**
-     * @return the auto-correction for this world, or null if none.
+     * @return the auto-correction for this word, or null if none.
      */
     public CharSequence getAutoCorrectionOrNull() {
         return mAutoCorrection;