Merge "Utilize KeyboardSet XML definitions"
diff --git a/Android.mk b/Android.mk
index 8f1acc5..91b2fbb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -12,6 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-subdir-makefiles)
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/Android.mk b/java/Android.mk
index 43168e5..36ff506 100644
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2011 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.
+
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a67e4c9..34699a4 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -203,7 +203,6 @@
     private boolean mApplicationSpecifiedCompletionOn;
 
     private WordComposer mWordComposer = new WordComposer();
-    private CharSequence mBestWord;
     private boolean mHasUncommittedTypedChars;
 
     private int mCorrectionMode;
@@ -1019,7 +1018,7 @@
                     .setHasMinimalSuggestion(false);
             // When in fullscreen mode, show completions generated by the application
             setSuggestions(builder.build());
-            mBestWord = null;
+            mWordComposer.deleteAutoCorrection();
             setSuggestionStripShown(true);
         }
     }
@@ -1415,12 +1414,7 @@
             final int length = mWordComposer.size();
             if (length > 0) {
                 mWordComposer.deleteLast();
-                final CharSequence textWithUnderline =
-                        mComposingStateManager.isAutoCorrectionIndicatorOn()
-                                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
-                                            this, mWordComposer.getTypedWord())
-                                : mWordComposer.getTypedWord();
-                ic.setComposingText(textWithUnderline, 1);
+                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
                 if (mWordComposer.size() == 0) {
                     mHasUncommittedTypedChars = false;
                     // Remaining size equals zero means we just erased the last character of the
@@ -1501,21 +1495,31 @@
     private void handleCharacter(final int primaryCode, final int[] keyCodes, final int x,
             final int y, final int spaceState) {
         mVoiceProxy.handleCharacter();
-
         final InputConnection ic = getCurrentInputConnection();
-        if (ic != null) ic.beginBatchEdit();
+        if (null != ic) ic.beginBatchEdit();
+        // TODO: if ic is null, does it make any sense to call this?
+        handleCharacterWhileInBatchEdit(primaryCode, keyCodes, x, y, spaceState, ic);
+        if (null != ic) ic.endBatchEdit();
+    }
+
+    // "ic" may be null without this crashing, but the behavior will be really strange
+    private void handleCharacterWhileInBatchEdit(final int primaryCode, final int[] keyCodes,
+            final int x, final int y, final int spaceState, final InputConnection ic) {
         if (SPACE_STATE_MAGIC == spaceState
                 && mSettingsValues.isMagicSpaceStripper(primaryCode)) {
-            removeTrailingSpaceWhileInBatchEdit(ic);
+            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();
@@ -1525,7 +1529,6 @@
         if (switcher.isShiftedOrShiftLocked()) {
             if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
                     || keyCodes[0] > Character.MAX_CODE_POINT) {
-                if (null != ic) ic.endBatchEdit();
                 return;
             }
             code = keyCodes[0];
@@ -1539,12 +1542,12 @@
                 } else {
                     // Some keys, such as [eszett], have upper case as multi-characters.
                     onTextInput(upperCaseString);
-                    if (null != ic) ic.endBatchEdit();
                     return;
                 }
             }
         }
-        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
@@ -1552,12 +1555,7 @@
                     mWordComposer.setAutoCapitalized(getCurrentAutoCapsState());
                     mComposingStateManager.onStartComposingText();
                 }
-                final CharSequence textWithUnderline =
-                        mComposingStateManager.isAutoCorrectionIndicatorOn()
-                                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
-                                        this, mWordComposer.getTypedWord())
-                                : mWordComposer.getTypedWord();
-                ic.setComposingText(textWithUnderline, 1);
+                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
             }
             mHandler.postUpdateSuggestions();
         } else {
@@ -1574,7 +1572,6 @@
         } else {
             Utils.Stats.onNonSeparator((char)code, x, y);
         }
-        if (null != ic) ic.endBatchEdit();
     }
 
     private void handleSeparator(final int primaryCode, final int x, final int y,
@@ -1611,6 +1608,21 @@
             }
         }
 
+        if (pickedDefault) {
+            final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+            final String typedWord = mWordComposer.getTypedWord();
+            if (TextUtils.isEmpty(typedWord)) {
+                throw new RuntimeException("We have non-committed chars but the typed word "
+                        + "is empty? Impossible! I must commit suicide.");
+            }
+            if (!typedWord.equals(autoCorrection)) {
+                // This will make the correction flash for a short while as a visual clue
+                // to the user that auto-correction happened.
+                InputConnectionCompatUtils.commitCorrection(
+                        ic, mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection);
+            }
+        }
+
         final boolean swapMagicSpace;
         if (Keyboard.CODE_ENTER == primaryCode && (SPACE_STATE_MAGIC == spaceState
                 || SPACE_STATE_SWAP_PUNCTUATION == spaceState)) {
@@ -1658,19 +1670,18 @@
 
         Utils.Stats.onSeparator((char)primaryCode, x, y);
 
-        if (pickedDefault) {
-            CharSequence typedWord = mWordComposer.getTypedWord();
-            if (!TextUtils.isEmpty(typedWord) && !typedWord.equals(mBestWord)) {
-                InputConnectionCompatUtils.commitCorrection(
-                        ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord);
-            }
-        }
         mKeyboardSwitcher.updateShiftState();
         if (ic != null) {
             ic.endBatchEdit();
         }
     }
 
+    private CharSequence getTextWithUnderline(final CharSequence text) {
+        return mComposingStateManager.isAutoCorrectionIndicatorOn()
+                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
+                : mWordComposer.getTypedWord();
+    }
+
     private void handleClose() {
         commitTyped(getCurrentInputConnection());
         mVoiceProxy.handleClose();
@@ -1744,18 +1755,21 @@
                     mComposingStateManager.isAutoCorrectionIndicatorOn();
             final boolean newAutoCorrectionIndicator = Utils.willAutoCorrect(words);
             if (oldAutoCorrectionIndicator != newAutoCorrectionIndicator) {
-                if (LatinImeLogger.sDBG) {
+                mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
+                if (DEBUG) {
                     Log.d(TAG, "Flip the indicator. " + oldAutoCorrectionIndicator
                             + " -> " + newAutoCorrectionIndicator);
+                    if (newAutoCorrectionIndicator
+                            != mComposingStateManager.isAutoCorrectionIndicatorOn()) {
+                        throw new RuntimeException("Couldn't flip the indicator! We are not "
+                                + "composing a word right now.");
+                    }
                 }
-                final CharSequence textWithUnderline = newAutoCorrectionIndicator
-                        ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
-                                this, mWordComposer.getTypedWord())
-                        : mWordComposer.getTypedWord();
+                final CharSequence textWithUnderline =
+                        getTextWithUnderline(mWordComposer.getTypedWord());
                 if (!TextUtils.isEmpty(textWithUnderline)) {
                     ic.setComposingText(textWithUnderline, 1);
                 }
-                mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
             }
         }
     }
@@ -1775,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;
@@ -1785,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
@@ -1812,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
@@ -1848,14 +1861,15 @@
         setSuggestions(suggestedWords);
         if (suggestedWords.size() > 0) {
             if (shouldBlockAutoCorrectionBySafetyNet) {
-                mBestWord = typedWord;
+                mWordComposer.setAutoCorrection(typedWord);
             } else if (suggestedWords.hasAutoCorrectionWord()) {
-                mBestWord = suggestedWords.getWord(1);
+                mWordComposer.setAutoCorrection(suggestedWords.getWord(1));
             } else {
-                mBestWord = typedWord;
+                mWordComposer.setAutoCorrection(typedWord);
             }
         } else {
-            mBestWord = null;
+            // TODO: replace with mWordComposer.deleteAutoCorrection()?
+            mWordComposer.setAutoCorrection(null);
         }
         setSuggestionStripShown(isSuggestionsStripVisible());
     }
@@ -1866,16 +1880,17 @@
             mHandler.cancelUpdateSuggestions();
             updateSuggestions();
         }
-        if (mBestWord != null && mBestWord.length() > 0) {
-            Utils.Stats.onAutoCorrection(mWordComposer.getTypedWord(), mBestWord.toString(),
-                    separatorCode);
+        final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+        if (autoCorrection != null) {
+            final String typedWord = mWordComposer.getTypedWord();
+            Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCode);
             mExpectingUpdateSelection = true;
-            commitBestWord(mBestWord);
-            if (!mBestWord.equals(mWordComposer.getTypedWord())) {
-                mWordSavedForAutoCorrectCancellation = mBestWord.toString();
+            commitBestWord(autoCorrection);
+            if (!autoCorrection.equals(typedWord)) {
+                mWordSavedForAutoCorrectCancellation = autoCorrection.toString();
             }
             // Add the word to the user unigram dictionary if it's not a known word
-            addToUserUnigramAndBigramDictionaries(mBestWord,
+            addToUserUnigramAndBigramDictionaries(autoCorrection,
                     UserUnigramDictionary.FREQUENCY_FOR_TYPED);
             return true;
         }
@@ -1947,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
@@ -2154,8 +2171,6 @@
     private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
             final CharSequence word) {
         mWordComposer.setComposingWord(word, mKeyboardSwitcher.getLatinKeyboard());
-        // mBestWord will be set appropriately by updateSuggestions() called by the handler
-        mBestWord = null;
         mHasUncommittedTypedChars = true;
         mComposingStateManager.onStartComposingText();
         ic.deleteSurroundingText(word.length(), 0);
@@ -2226,8 +2241,11 @@
         // Here we test whether we indeed have a period and a space before us. This should not
         // be needed, but it's there just in case something went wrong.
         final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
-        if (!". ".equals(textBeforeCursor))
-            return false;
+        if (!". ".equals(textBeforeCursor)) {
+            // We should not have come here if we aren't just after a ". ".
+            throw new RuntimeException("Tried to revert double-space combo but we didn't find "
+                    + "\". \" just before the cursor.");
+        }
         ic.beginBatchEdit();
         ic.deleteSurroundingText(2, 0);
         ic.commitText("  ", 1);
@@ -2241,8 +2259,11 @@
         final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
         // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
         // enter surrogate pairs this code will have been removed.
-        if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))
-            return false;
+        if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1)) {
+            // We should not have come here if the text before the cursor is not a space.
+            throw new RuntimeException("Tried to revert a swap of punctiation but we didn't "
+                    + "find a space just before the cursor.");
+        }
         ic.beginBatchEdit();
         ic.deleteSurroundingText(2, 0);
         ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
@@ -2383,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 dfb00c8..8bbcff7 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -41,6 +41,8 @@
     private int[] mYCoordinates;
 
     private StringBuilder mTypedWord;
+    // An auto-correction for this word out of the dictionary.
+    private CharSequence mAutoCorrection;
 
     private int mCapsCount;
 
@@ -60,6 +62,7 @@
         mXCoordinates = new int[N];
         mYCoordinates = new int[N];
         mTrailingSingleQuotesCount = 0;
+        mAutoCorrection = null;
     }
 
     public WordComposer(WordComposer source) {
@@ -75,6 +78,7 @@
         mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
         mAutoCapitalized = source.mAutoCapitalized;
         mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
+        mAutoCorrection = null;
     }
 
     /**
@@ -86,6 +90,7 @@
         mCapsCount = 0;
         mIsFirstCharCapitalized = false;
         mTrailingSingleQuotesCount = 0;
+        mAutoCorrection = null;
     }
 
     /**
@@ -140,6 +145,7 @@
         } else {
             mTrailingSingleQuotesCount = 0;
         }
+        mAutoCorrection = null;
     }
 
     /**
@@ -173,6 +179,7 @@
             int codePoint = word.charAt(i);
             addKeyInfo(codePoint, keyboard, keyDetector);
         }
+        mAutoCorrection = null;
     }
 
     /**
@@ -224,11 +231,12 @@
                 ++mTrailingSingleQuotesCount;
             }
         }
+        mAutoCorrection = null;
     }
 
     /**
      * Returns the word as it was typed, without any correction applied.
-     * @return the word that was typed so far
+     * @return the word that was typed so far. Never returns null.
      */
     public String getTypedWord() {
         return mTypedWord.toString();
@@ -277,4 +285,25 @@
     public boolean isAutoCapitalized() {
         return mAutoCapitalized;
     }
+
+    /**
+     * Sets the auto-correction for this word.
+     */
+    public void setAutoCorrection(final CharSequence correction) {
+        mAutoCorrection = correction;
+    }
+
+    /**
+     * Remove any auto-correction that may have been set.
+     */
+    public void deleteAutoCorrection() {
+        mAutoCorrection = null;
+    }
+
+    /**
+     * @return the auto-correction for this word, or null if none.
+     */
+    public CharSequence getAutoCorrectionOrNull() {
+        return mAutoCorrection;
+    }
 }
diff --git a/native/Android.mk b/native/Android.mk
index 5dcc1e5..5053e7d 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -1,65 +1 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_CFLAGS += -Werror -Wall
-
-# To suppress compiler warnings for unused variables/functions used for debug features etc.
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
-
-LOCAL_SRC_FILES := \
-    jni/com_android_inputmethod_keyboard_ProximityInfo.cpp \
-    jni/com_android_inputmethod_latin_BinaryDictionary.cpp \
-    jni/jni_common.cpp \
-    src/basechars.cpp \
-    src/bigram_dictionary.cpp \
-    src/char_utils.cpp \
-    src/correction.cpp \
-    src/dictionary.cpp \
-    src/proximity_info.cpp \
-    src/unigram_dictionary.cpp
-
-#FLAG_DBG := true
-#FLAG_DO_PROFILE := true
-
-TARGETING_UNBUNDLED_FROYO := true
-
-ifeq ($(TARGET_ARCH), x86)
-    TARGETING_UNBUNDLED_FROYO := false
-endif
-
-ifeq ($(FLAG_DBG), true)
-    TARGETING_UNBUNDLED_FROYO := false
-endif
-
-ifeq ($(FLAG_DO_PROFILE), true)
-    TARGETING_UNBUNDLED_FROYO := false
-endif
-
-ifeq ($(TARGETING_UNBUNDLED_FROYO), true)
-    LOCAL_NDK_VERSION := 4
-    LOCAL_SDK_VERSION := 8
-endif
-
-LOCAL_MODULE := libjni_latinime
-
-LOCAL_MODULE_TAGS := user
-
-# For STL
-LOCAL_C_INCLUDES += external/stlport/stlport bionic
-LOCAL_SHARED_LIBRARIES += libstlport
-
-ifeq ($(FLAG_DO_PROFILE), true)
-    $(warning Making profiling version of native library)
-    LOCAL_CFLAGS += -DFLAG_DO_PROFILE
-    LOCAL_SHARED_LIBRARIES += libcutils libutils
-else # FLAG_DO_PROFILE
-ifeq ($(FLAG_DBG), true)
-    $(warning Making debug version of native library)
-    LOCAL_CFLAGS += -DFLAG_DBG
-    LOCAL_SHARED_LIBRARIES += libcutils libutils
-endif # FLAG_DBG
-endif # FLAG_DO_PROFILE
-
-include $(BUILD_SHARED_LIBRARY)
+include $(call all-subdir-makefiles)
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
new file mode 100644
index 0000000..c4adbfa
--- /dev/null
+++ b/native/jni/Android.mk
@@ -0,0 +1,87 @@
+# Copyright (C) 2011 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LATIN_IME_SRC_DIR := ../src
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_SRC_DIR)
+
+LOCAL_CFLAGS += -Werror -Wall
+
+# To suppress compiler warnings for unused variables/functions used for debug features etc.
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
+
+LATIN_IME_JNI_SRC_FILES := \
+    com_android_inputmethod_keyboard_ProximityInfo.cpp \
+    com_android_inputmethod_latin_BinaryDictionary.cpp \
+    jni_common.cpp
+
+LATIN_IME_CORE_SRC_FILES := \
+    basechars.cpp \
+    bigram_dictionary.cpp \
+    char_utils.cpp \
+    correction.cpp \
+    dictionary.cpp \
+    proximity_info.cpp \
+    unigram_dictionary.cpp
+
+LOCAL_SRC_FILES := \
+    $(LATIN_IME_JNI_SRC_FILES) \
+    $(addprefix $(LATIN_IME_SRC_DIR)/,$(LATIN_IME_CORE_SRC_FILES))
+
+#FLAG_DBG := true
+#FLAG_DO_PROFILE := true
+
+TARGETING_UNBUNDLED_FROYO := true
+
+ifeq ($(TARGET_ARCH), x86)
+    TARGETING_UNBUNDLED_FROYO := false
+endif
+
+ifeq ($(FLAG_DBG), true)
+    TARGETING_UNBUNDLED_FROYO := false
+endif
+
+ifeq ($(FLAG_DO_PROFILE), true)
+    TARGETING_UNBUNDLED_FROYO := false
+endif
+
+ifeq ($(TARGETING_UNBUNDLED_FROYO), true)
+    LOCAL_NDK_VERSION := 4
+    LOCAL_SDK_VERSION := 8
+endif
+
+LOCAL_MODULE := libjni_latinime
+
+LOCAL_MODULE_TAGS := user
+
+# For STL
+LOCAL_C_INCLUDES += external/stlport/stlport bionic
+LOCAL_SHARED_LIBRARIES += libstlport
+
+ifeq ($(FLAG_DO_PROFILE), true)
+    $(warning Making profiling version of native library)
+    LOCAL_CFLAGS += -DFLAG_DO_PROFILE
+    LOCAL_SHARED_LIBRARIES += libcutils libutils
+else # FLAG_DO_PROFILE
+ifeq ($(FLAG_DBG), true)
+    $(warning Making debug version of native library)
+    LOCAL_CFLAGS += -DFLAG_DBG
+    LOCAL_SHARED_LIBRARIES += libcutils libutils
+endif # FLAG_DBG
+endif # FLAG_DO_PROFILE
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/native/jni/Application.mk b/native/jni/Application.mk
new file mode 100644
index 0000000..caf3b26
--- /dev/null
+++ b/native/jni/Application.mk
@@ -0,0 +1 @@
+APP_STL := stlport_static
diff --git a/tests/Android.mk b/tests/Android.mk
index 658e8e2..6634070 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2011 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.
+
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
diff --git a/tools/Android.mk b/tools/Android.mk
index 8f1acc5..91b2fbb 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -12,6 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-subdir-makefiles)