Merge "Add ExpectedKeyboardBuilder for testing keyboard layouts"
diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java
index 5e046a8..3f709a6 100644
--- a/java/src/com/android/inputmethod/event/InputTransaction.java
+++ b/java/src/com/android/inputmethod/event/InputTransaction.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.event;
 
+import com.android.inputmethod.latin.settings.SettingsValues;
+
 /**
  * An object encapsulating a single transaction for input.
  */
@@ -30,12 +32,28 @@
     public static final int SHIFT_UPDATE_LATER = 2;
 
     // Initial conditions
+    public final SettingsValues mSettingsValues;
+    // If the key inserts a code point, mKeyCode is always equal to the code points. Otherwise,
+    // it's always a code that may not be a code point, typically a negative number.
+    public final int mKeyCode;
+    public final int mX; // Pressed x-coordinate, or one of Constants.*_COORDINATE
+    public final int mY; // Pressed y-coordinate, or one of Constants.*_COORDINATE
+    public final long mTimestamp;
+    public final int mSpaceState;
     public final int mShiftState;
 
     // Outputs
     private int mRequiredShiftUpdate = SHIFT_NO_UPDATE;
 
-    public InputTransaction(final int shiftState) {
+    public InputTransaction(final SettingsValues settingsValues, final int keyCode,
+            final int x, final int y, final long timestamp, final int spaceState,
+            final int shiftState) {
+        mSettingsValues = settingsValues;
+        mKeyCode = keyCode;
+        mX = x;
+        mY = y;
+        mTimestamp = timestamp;
+        mSpaceState = spaceState;
         mShiftState = shiftState;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 59cf64d..3539a87 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -323,7 +323,7 @@
     }
 
     // Note that we need primaryCode argument because the keyboard may in shifted state and the
-    // primaryCode is different from {@link Key#mCode}.
+    // primaryCode is different from {@link Key#mKeyCode}.
     private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
             final int y, final long eventTime) {
         final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
@@ -360,7 +360,7 @@
     }
 
     // Note that we need primaryCode argument because the keyboard may be in shifted state and the
-    // primaryCode is different from {@link Key#mCode}.
+    // primaryCode is different from {@link Key#mKeyCode}.
     private void callListenerOnRelease(final Key key, final int primaryCode,
             final boolean withSliding) {
         // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 05e6ef0..a3a329a 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -199,7 +199,6 @@
         return true;
     }
 
-    @UsedForTesting
     public DictionaryHeader getHeader() throws UnsupportedFormatException {
         if (mNativeDict == 0) {
             return null;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index acbd919..4c49cb3 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -21,10 +21,9 @@
 import android.content.res.AssetFileDescriptor;
 import android.util.Log;
 
-import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
 import com.android.inputmethod.latin.utils.LocaleUtils;
@@ -226,12 +225,10 @@
     // ## HACK ## we prevent usage of a dictionary before version 18. The reason for this is, since
     // those do not include whitelist entries, the new code with an old version of the dictionary
     // would lose whitelist functionality.
-    private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) {
+    private static boolean hackCanUseDictionaryFile(final Locale locale, final File file) {
         try {
             // Read the version of the file
-            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(f, 0, f.length());
-            final DictionaryHeader header = dictDecoder.readHeader();
-
+            final DictionaryHeader header = BinaryDictionaryUtils.getHeader(file);
             final String version = header.mDictionaryOptions.mAttributes.get(VERSION_KEY);
             if (null == version) {
                 // No version in the options : the format is unexpected
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index d1ff714..e71723a 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -144,6 +144,7 @@
 
     public static final int NOT_A_CODE = -1;
     public static final int NOT_A_CURSOR_POSITION = -1;
+    // TODO: replace the following constants with state in InputTransaction?
     public static final int NOT_A_COORDINATE = -1;
     public static final int SUGGESTION_STRIP_COORDINATE = -2;
     public static final int SPELL_CHECKER_COORDINATE = -3;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 44353ba..a9e5480 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -59,6 +59,7 @@
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
 import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
+import com.android.inputmethod.event.InputTransaction;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.KeyboardId;
@@ -1265,8 +1266,18 @@
             mSubtypeSwitcher.switchToShortcutIME(this);
             // Still call the *#onCodeInput methods for readability.
         }
-        mInputLogic.onCodeInput(codeToSend, keyX, keyY, mSettings.getCurrent(), mHandler,
-                mKeyboardSwitcher);
+        final InputTransaction completeInputTransaction =
+                mInputLogic.onCodeInput(mSettings.getCurrent(), codeToSend, keyX, keyY,
+                        mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
+        switch (completeInputTransaction.getRequiredShiftUpdate()) {
+            case InputTransaction.SHIFT_UPDATE_LATER:
+                mHandler.postUpdateShiftState();
+                break;
+            case InputTransaction.SHIFT_UPDATE_NOW:
+                mKeyboardSwitcher.updateShiftState();
+                break;
+            default: // SHIFT_NO_UPDATE
+        }
         mKeyboardSwitcher.onCodeInput(codePoint);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index daaac59..dd9d6e8 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -205,9 +205,9 @@
             LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
             // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
             final int primaryCode = suggestion.charAt(0);
-            onCodeInput(primaryCode,
+            onCodeInput(settingsValues, primaryCode,
                     Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE,
-                    settingsValues, handler, keyboardSwitcher);
+                    keyboardSwitcher.getKeyboardShiftMode(), handler);
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
                         false /* isBatchMode */, suggestedWords.mIsPrediction);
@@ -353,49 +353,48 @@
      * Typically, this is called whenever a key is pressed on the software keyboard. This is not
      * the entry point for gesture input; see the onBatchInput* family of functions for this.
      *
+     * @param settingsValues the current settings values.
      * @param code the code to handle. It may be a code point, or an internal key code.
      * @param x the x-coordinate where the user pressed the key, or NOT_A_COORDINATE.
      * @param y the y-coordinate where the user pressed the key, or NOT_A_COORDINATE.
+     * @param keyboardShiftMode the current shift mode of the keyboard, as returned by
+     *     {@link com.android.inputmethod.keyboard.KeyboardSwitcher#getKeyboardShiftMode()}
+     * @return the complete transaction object
      */
-    public void onCodeInput(final int code, final int x, final int y,
-            final SettingsValues settingsValues,
-            // TODO: remove these two arguments
-            final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) {
+    public InputTransaction onCodeInput(final SettingsValues settingsValues, final int code,
+            final int x, final int y, final int keyboardShiftMode,
+            // TODO: remove this argument
+            final LatinIME.UIHandler handler) {
+        final InputTransaction inputTransaction = new InputTransaction(settingsValues, code, x, y,
+                SystemClock.uptimeMillis(), mSpaceState,
+                getActualCapsMode(settingsValues, keyboardShiftMode));
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-            ResearchLogger.latinIME_onCodeInput(code, x, y);
+            ResearchLogger.latinIME_onCodeInput(inputTransaction.mKeyCode,
+                    inputTransaction.mX, inputTransaction.mY);
         }
-        final long when = SystemClock.uptimeMillis();
-        final InputTransaction inputTransaction = new InputTransaction(
-                getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
-        if (code != Constants.CODE_DELETE
-                || when > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
+        if (inputTransaction.mKeyCode != Constants.CODE_DELETE
+                || inputTransaction.mTimestamp > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
             mDeleteCount = 0;
         }
-        mLastKeyTime = when;
+        mLastKeyTime = inputTransaction.mTimestamp;
         mConnection.beginBatchEdit();
-        // The space state depends only on the last character pressed and its own previous
-        // state. Here, we revert the space state to neutral if the key is actually modifying
-        // the input contents (any non-shift key), which is what we should do for
-        // all inputs that do not result in a special state. Each character handling is then
-        // free to override the state as they see fit.
-        final int spaceState = mSpaceState;
         if (!mWordComposer.isComposingWord()) {
             mIsAutoCorrectionIndicatorOn = false;
         }
 
         // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
-        if (code != Constants.CODE_SPACE) {
+        if (inputTransaction.mKeyCode != Constants.CODE_SPACE) {
             handler.cancelDoubleSpacePeriodTimer();
         }
 
         boolean didAutoCorrect = false;
-        switch (code) {
+        switch (inputTransaction.mKeyCode) {
         case Constants.CODE_DELETE:
-            handleBackspace(settingsValues, spaceState, inputTransaction, handler);
-            LatinImeLogger.logOnDelete(x, y);
+            handleBackspace(inputTransaction, handler);
+            LatinImeLogger.logOnDelete(inputTransaction.mX, inputTransaction.mY);
             break;
         case Constants.CODE_SHIFT:
-            performRecapitalization(settingsValues);
+            performRecapitalization(inputTransaction.mSettingsValues);
             inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
             break;
         case Constants.CODE_CAPSLOCK:
@@ -450,41 +449,35 @@
             } else {
                 // No action label, and the action from imeOptions is NONE: this is a regular
                 // enter key that should input a carriage return.
-                didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER,
-                        x, y, spaceState, inputTransaction, handler);
+                didAutoCorrect = handleNonSpecialCharacter(inputTransaction, handler);
             }
             break;
         case Constants.CODE_SHIFT_ENTER:
-            didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER,
-                    x, y, spaceState, inputTransaction, handler);
+            // TODO: remove this object
+            final InputTransaction tmpTransaction = new InputTransaction(
+                    inputTransaction.mSettingsValues, inputTransaction.mKeyCode,
+                    inputTransaction.mX, inputTransaction.mY, inputTransaction.mTimestamp,
+                    inputTransaction.mSpaceState, inputTransaction.mShiftState);
+            didAutoCorrect = handleNonSpecialCharacter(tmpTransaction, handler);
             break;
         case Constants.CODE_ALPHA_FROM_EMOJI:
             // Note: Switching back from Emoji keyboard to the main keyboard is being handled in
             // {@link KeyboardState#onCodeInput(int,int)}.
             break;
         default:
-            didAutoCorrect = handleNonSpecialCharacter(settingsValues,
-                    code, x, y, spaceState, inputTransaction, handler);
+            didAutoCorrect = handleNonSpecialCharacter(inputTransaction, handler);
             break;
         }
         // Reset after any single keystroke, except shift, capslock, and symbol-shift
-        if (!didAutoCorrect && code != Constants.CODE_SHIFT
-                && code != Constants.CODE_CAPSLOCK
-                && code != Constants.CODE_SWITCH_ALPHA_SYMBOL)
+        if (!didAutoCorrect && inputTransaction.mKeyCode != Constants.CODE_SHIFT
+                && inputTransaction.mKeyCode != Constants.CODE_CAPSLOCK
+                && inputTransaction.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
             mLastComposedWord.deactivate();
-        if (Constants.CODE_DELETE != code) {
+        if (Constants.CODE_DELETE != inputTransaction.mKeyCode) {
             mEnteredText = null;
         }
         mConnection.endBatchEdit();
-        switch (inputTransaction.getRequiredShiftUpdate()) {
-        case InputTransaction.SHIFT_UPDATE_LATER:
-            mLatinIME.mHandler.postUpdateShiftState();
-            break;
-        case InputTransaction.SHIFT_UPDATE_NOW:
-            keyboardSwitcher.updateShiftState();
-            break;
-        default: // SHIFT_NO_UPDATE
-        }
+        return inputTransaction;
     }
 
     public void onStartBatchInput(final SettingsValues settingsValues,
@@ -628,33 +621,26 @@
      * manage keyboard-related stuff like shift, language switch, settings, layout switch, or
      * any key that results in multiple code points like the ".com" key.
      *
-     * @param settingsValues The current settings values.
-     * @param codePoint the code point associated with the key.
-     * @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
-     * @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
-     * @param spaceState the space state at start of the batch input.
      * @param inputTransaction The transaction in progress.
      * @return whether this caused an auto-correction to happen.
      */
-    private boolean handleNonSpecialCharacter(final SettingsValues settingsValues,
-            final int codePoint, final int x, final int y, final int spaceState,
-            final InputTransaction inputTransaction,
+    private boolean handleNonSpecialCharacter(final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
         mSpaceState = SpaceState.NONE;
         final boolean didAutoCorrect;
-        if (settingsValues.isWordSeparator(codePoint)
-                || Character.getType(codePoint) == Character.OTHER_SYMBOL) {
-            didAutoCorrect = handleSeparator(settingsValues, codePoint,
-                    Constants.SUGGESTION_STRIP_COORDINATE == x, spaceState, inputTransaction,
-                    handler);
-            if (settingsValues.mIsInternal) {
-                LatinImeLoggerUtils.onSeparator((char)codePoint, x, y);
+        if (inputTransaction.mSettingsValues.isWordSeparator(inputTransaction.mKeyCode)
+                || Character.getType(inputTransaction.mKeyCode) == Character.OTHER_SYMBOL) {
+            didAutoCorrect = handleSeparator(inputTransaction,
+                    Constants.SUGGESTION_STRIP_COORDINATE == inputTransaction.mX, handler);
+            if (inputTransaction.mSettingsValues.mIsInternal) {
+                LatinImeLoggerUtils.onSeparator((char)inputTransaction.mKeyCode,
+                        inputTransaction.mX, inputTransaction.mY);
             }
         } else {
             didAutoCorrect = false;
-            if (SpaceState.PHANTOM == spaceState) {
-                if (settingsValues.mIsInternal) {
+            if (SpaceState.PHANTOM == inputTransaction.mSpaceState) {
+                if (inputTransaction.mSettingsValues.mIsInternal) {
                     if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) {
                         LatinImeLoggerUtils.onAutoCorrection("", mWordComposer.getTypedWord(), " ",
                                 mWordComposer);
@@ -666,11 +652,10 @@
                     resetEntireInputState(mConnection.getExpectedSelectionStart(),
                             mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */);
                 } else {
-                    commitTyped(settingsValues, LastComposedWord.NOT_A_SEPARATOR);
+                    commitTyped(inputTransaction.mSettingsValues, LastComposedWord.NOT_A_SEPARATOR);
                 }
             }
-            handleNonSeparator(settingsValues, codePoint, x, y, spaceState,
-                    inputTransaction, handler);
+            handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction, handler);
         }
         return didAutoCorrect;
     }
@@ -678,14 +663,9 @@
     /**
      * Handle a non-separator.
      * @param settingsValues The current settings values.
-     * @param codePoint the code point associated with the key.
-     * @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
-     * @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
-     * @param spaceState the space state at start of the batch input.
      * @param inputTransaction The transaction in progress.
      */
     private void handleNonSeparator(final SettingsValues settingsValues,
-            final int codePoint, final int x, final int y, final int spaceState,
             final InputTransaction inputTransaction,
             // TODO: Remove this argument
             final LatinIME.UIHandler handler) {
@@ -696,7 +676,8 @@
 
         // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead.
         // See onStartBatchInput() to see how to do it.
-        if (SpaceState.PHANTOM == spaceState && !settingsValues.isWordConnector(codePoint)) {
+        if (SpaceState.PHANTOM == inputTransaction.mSpaceState
+                && !settingsValues.isWordConnector(inputTransaction.mKeyCode)) {
             if (isComposingWord) {
                 // Sanity check
                 throw new RuntimeException("Should not be composing here");
@@ -718,7 +699,7 @@
         if (!isComposingWord
         // We only start composing if this is a word code point. Essentially that means it's a
         // a letter or a word connector.
-                && settingsValues.isWordCodePoint(codePoint)
+                && settingsValues.isWordCodePoint(inputTransaction.mKeyCode)
         // We never go into composing state if suggestions are not requested.
                 && settingsValues.isSuggestionsRequested() &&
         // In languages with spaces, we only start composing a word when we are not already
@@ -729,8 +710,8 @@
             // the character is a single quote or a dash. The idea here is, single quote and dash
             // are not separators and they should be treated as normal characters, except in the
             // first position where they should not start composing a word.
-            isComposingWord = (Constants.CODE_SINGLE_QUOTE != codePoint
-                    && Constants.CODE_DASH != codePoint);
+            isComposingWord = (Constants.CODE_SINGLE_QUOTE != inputTransaction.mKeyCode
+                    && Constants.CODE_DASH != inputTransaction.mKeyCode);
             // Here we don't need to reset the last composed word. It will be reset
             // when we commit this one, if we ever do; if on the other hand we backspace
             // it entirely and resume suggestions on the previous word, we'd like to still
@@ -738,7 +719,7 @@
             resetComposingState(false /* alsoResetLastComposedWord */);
         }
         if (isComposingWord) {
-            mWordComposer.add(codePoint, x, y);
+            mWordComposer.add(inputTransaction.mKeyCode, inputTransaction.mX, inputTransaction.mY);
             // If it's the first letter, make note of auto-caps state
             if (mWordComposer.size() == 1) {
                 // We pass 1 to getPreviousWordForSuggestion because we were not composing a word
@@ -750,10 +731,10 @@
             mConnection.setComposingText(getTextWithUnderline(
                     mWordComposer.getTypedWord()), 1);
         } else {
-            final boolean swapWeakSpace = maybeStripSpace(settingsValues,
-                    codePoint, spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x);
+            final boolean swapWeakSpace = maybeStripSpace(inputTransaction,
+                    Constants.SUGGESTION_STRIP_COORDINATE == inputTransaction.mX);
 
-            sendKeyCodePoint(settingsValues, codePoint);
+            sendKeyCodePoint(settingsValues, inputTransaction.mKeyCode);
 
             if (swapWeakSpace) {
                 swapSwapperAndSpace(inputTransaction);
@@ -764,28 +745,26 @@
         }
         handler.postUpdateSuggestionStrip();
         if (settingsValues.mIsInternal) {
-            LatinImeLoggerUtils.onNonSeparator((char)codePoint, x, y);
+            LatinImeLoggerUtils.onNonSeparator((char)inputTransaction.mKeyCode, inputTransaction.mX,
+                    inputTransaction.mY);
         }
     }
 
     /**
      * Handle input of a separator code point.
-     * @param settingsValues The current settings values.
-     * @param codePoint the code point associated with the key.
-     * @param isFromSuggestionStrip whether this code point comes from the suggestion strip.
-     * @param spaceState the space state at start of the batch input.
      * @param inputTransaction The transaction in progress.
+     * @param isFromSuggestionStrip whether this code point comes from the suggestion strip.
      * @return whether this caused an auto-correction to happen.
      */
-    private boolean handleSeparator(final SettingsValues settingsValues,
-            final int codePoint, final boolean isFromSuggestionStrip, final int spaceState,
-            final InputTransaction inputTransaction,
+    private boolean handleSeparator(final InputTransaction inputTransaction,
+            final boolean isFromSuggestionStrip,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
         boolean didAutoCorrect = false;
         // We avoid sending spaces in languages without spaces if we were composing.
-        final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint
-                && !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
+        final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == inputTransaction.mKeyCode
+                && !inputTransaction.mSettingsValues.mSpacingAndPunctuations
+                        .mCurrentLanguageHasSpaces
                 && mWordComposer.isComposingWord();
         if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
             // If we are in the middle of a recorrection, we need to commit the recorrection
@@ -795,47 +774,50 @@
         }
         // isComposingWord() may have changed since we stored wasComposing
         if (mWordComposer.isComposingWord()) {
-            if (settingsValues.mCorrectionEnabled) {
+            if (inputTransaction.mSettingsValues.mCorrectionEnabled) {
                 final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
-                        : StringUtils.newSingleCodePointString(codePoint);
-                commitCurrentAutoCorrection(settingsValues, separator, handler);
+                        : StringUtils.newSingleCodePointString(inputTransaction.mKeyCode);
+                commitCurrentAutoCorrection(inputTransaction.mSettingsValues, separator, handler);
                 didAutoCorrect = true;
             } else {
-                commitTyped(settingsValues, StringUtils.newSingleCodePointString(codePoint));
+                commitTyped(inputTransaction.mSettingsValues,
+                        StringUtils.newSingleCodePointString(inputTransaction.mKeyCode));
             }
         }
 
-        final boolean swapWeakSpace = maybeStripSpace(settingsValues, codePoint, spaceState,
-                isFromSuggestionStrip);
+        final boolean swapWeakSpace = maybeStripSpace(inputTransaction, isFromSuggestionStrip);
 
-        final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint
+        final boolean isInsideDoubleQuoteOrAfterDigit =
+                Constants.CODE_DOUBLE_QUOTE == inputTransaction.mKeyCode
                 && mConnection.isInsideDoubleQuoteOrAfterDigit();
 
         final boolean needsPrecedingSpace;
-        if (SpaceState.PHANTOM != spaceState) {
+        if (SpaceState.PHANTOM != inputTransaction.mSpaceState) {
             needsPrecedingSpace = false;
-        } else if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
+        } else if (Constants.CODE_DOUBLE_QUOTE == inputTransaction.mKeyCode) {
             // Double quotes behave like they are usually preceded by space iff we are
             // not inside a double quote or after a digit.
             needsPrecedingSpace = !isInsideDoubleQuoteOrAfterDigit;
         } else {
-            needsPrecedingSpace = settingsValues.isUsuallyPrecededBySpace(codePoint);
+            needsPrecedingSpace = inputTransaction.mSettingsValues.isUsuallyPrecededBySpace(
+                    inputTransaction.mKeyCode);
         }
 
         if (needsPrecedingSpace) {
-            promotePhantomSpace(settingsValues);
+            promotePhantomSpace(inputTransaction.mSettingsValues);
         }
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-            ResearchLogger.latinIME_handleSeparator(codePoint, mWordComposer.isComposingWord());
+            ResearchLogger.latinIME_handleSeparator(inputTransaction.mKeyCode,
+                    mWordComposer.isComposingWord());
         }
 
         if (!shouldAvoidSendingCode) {
-            sendKeyCodePoint(settingsValues, codePoint);
+            sendKeyCodePoint(inputTransaction.mSettingsValues, inputTransaction.mKeyCode);
         }
 
-        if (Constants.CODE_SPACE == codePoint) {
-            if (settingsValues.isSuggestionsRequested()) {
-                if (maybeDoubleSpacePeriod(settingsValues, handler)) {
+        if (Constants.CODE_SPACE == inputTransaction.mKeyCode) {
+            if (inputTransaction.mSettingsValues.isSuggestionsRequested()) {
+                if (maybeDoubleSpacePeriod(inputTransaction.mSettingsValues, handler)) {
                     inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
                     mSpaceState = SpaceState.DOUBLE;
                 } else if (!mSuggestedWords.isPunctuationSuggestions()) {
@@ -849,9 +831,10 @@
             if (swapWeakSpace) {
                 swapSwapperAndSpace(inputTransaction);
                 mSpaceState = SpaceState.SWAP_PUNCTUATION;
-            } else if ((SpaceState.PHANTOM == spaceState
-                    && settingsValues.isUsuallyFollowedBySpace(codePoint))
-                    || (Constants.CODE_DOUBLE_QUOTE == codePoint
+            } else if ((SpaceState.PHANTOM == inputTransaction.mSpaceState
+                    && inputTransaction.mSettingsValues.isUsuallyFollowedBySpace(
+                            inputTransaction.mKeyCode))
+                    || (Constants.CODE_DOUBLE_QUOTE == inputTransaction.mKeyCode
                             && isInsideDoubleQuoteOrAfterDigit)) {
                 // If we are in phantom space state, and the user presses a separator, we want to
                 // stay in phantom space state so that the next keypress has a chance to add the
@@ -878,12 +861,9 @@
 
     /**
      * Handle a press on the backspace key.
-     * @param settingsValues The current settings values.
-     * @param spaceState The space state at start of this batch edit.
      * @param inputTransaction The transaction in progress.
      */
-    private void handleBackspace(final SettingsValues settingsValues, final int spaceState,
-            final InputTransaction inputTransaction,
+    private void handleBackspace(final InputTransaction inputTransaction,
             // TODO: remove this argument
             final LatinIME.UIHandler handler) {
         mSpaceState = SpaceState.NONE;
@@ -922,10 +902,10 @@
             }
         } else {
             if (mLastComposedWord.canRevertCommit()) {
-                if (settingsValues.mIsInternal) {
+                if (inputTransaction.mSettingsValues.mIsInternal) {
                     LatinImeLoggerUtils.onAutoCorrectionCancellation();
                 }
-                revertCommit(settingsValues, handler);
+                revertCommit(inputTransaction.mSettingsValues, handler);
                 return;
             }
             if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
@@ -942,14 +922,14 @@
                 // reverting any autocorrect at this point. So we can safely return.
                 return;
             }
-            if (SpaceState.DOUBLE == spaceState) {
+            if (SpaceState.DOUBLE == inputTransaction.mSpaceState) {
                 handler.cancelDoubleSpacePeriodTimer();
                 if (mConnection.revertDoubleSpacePeriod()) {
                     // No need to reset mSpaceState, it has already be done (that's why we
                     // receive it as a parameter)
                     return;
                 }
-            } else if (SpaceState.SWAP_PUNCTUATION == spaceState) {
+            } else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
                 if (mConnection.revertSwapPunctuation()) {
                     // Likewise
                     return;
@@ -975,8 +955,8 @@
                     // This should never happen.
                     Log.e(TAG, "Backspace when we don't know the selection position");
                 }
-                if (settingsValues.isBeforeJellyBean() ||
-                        settingsValues.mInputAttributes.isTypeNull()) {
+                if (inputTransaction.mSettingsValues.isBeforeJellyBean() ||
+                        inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()) {
                     // There are two possible reasons to send a key event: either the field has
                     // type TYPE_NULL, in which case the keyboard should send events, or we are
                     // running in backward compatibility mode. Before Jelly bean, the keyboard
@@ -1022,11 +1002,12 @@
                     }
                 }
             }
-            if (settingsValues.isSuggestionStripVisible()
-                    && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
+            if (inputTransaction.mSettingsValues.isSuggestionStripVisible()
+                    && inputTransaction.mSettingsValues.mSpacingAndPunctuations
+                            .mCurrentLanguageHasSpaces
                     && !mConnection.isCursorFollowedByWordCharacter(
-                            settingsValues.mSpacingAndPunctuations)) {
-                restartSuggestionsOnWordTouchedByCursor(settingsValues,
+                            inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
+                restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
                         true /* includeResumedWordInSuggestions */);
             }
             // We just removed at least one character. We need to update the auto-caps state.
@@ -1064,22 +1045,28 @@
 
     /*
      * Strip a trailing space if necessary and returns whether it's a swap weak space situation.
-     * @param settingsValues The current settings values.
-     * @param codePoint The code point that is about to be inserted.
-     * @param spaceState The space state at start of this batch edit.
+     * @param inputTransaction The transaction in progress.
      * @param isFromSuggestionStrip Whether this code point is coming from the suggestion strip.
      * @return whether we should swap the space instead of removing it.
      */
-    private boolean maybeStripSpace(final SettingsValues settingsValues,
-            final int code, final int spaceState, final boolean isFromSuggestionStrip) {
-        if (Constants.CODE_ENTER == code && SpaceState.SWAP_PUNCTUATION == spaceState) {
+    private boolean maybeStripSpace(final InputTransaction inputTransaction,
+            final boolean isFromSuggestionStrip) {
+        if (Constants.CODE_ENTER == inputTransaction.mKeyCode &&
+                SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
             mConnection.removeTrailingSpace();
             return false;
         }
-        if ((SpaceState.WEAK == spaceState || SpaceState.SWAP_PUNCTUATION == spaceState)
+        if ((SpaceState.WEAK == inputTransaction.mSpaceState
+                || SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState)
                 && isFromSuggestionStrip) {
-            if (settingsValues.isUsuallyPrecededBySpace(code)) return false;
-            if (settingsValues.isUsuallyFollowedBySpace(code)) return true;
+            if (inputTransaction.mSettingsValues.isUsuallyPrecededBySpace(
+                    inputTransaction.mKeyCode)) {
+                return false;
+            }
+            if (inputTransaction.mSettingsValues.isUsuallyFollowedBySpace(
+                    inputTransaction.mKeyCode)) {
+                return true;
+            }
             mConnection.removeTrailingSpace();
         }
         return false;
@@ -1313,7 +1300,10 @@
                     SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
                     SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         }
-        if (!isResumableWord(settingsValues, typedWord)) return;
+        if (!isResumableWord(settingsValues, typedWord)) {
+            mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
+            return;
+        }
         int i = 0;
         for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) {
             for (final String s : span.getSuggestions()) {
@@ -1738,6 +1728,7 @@
      * @param settingsValues the current values of the settings.
      * @param codePoint the code point to send.
      */
+    // TODO: replace these two parameters with an InputTransaction
     private void sendKeyCodePoint(final SettingsValues settingsValues, final int codePoint) {
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.latinIME_sendKeyCodePoint(codePoint);
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 90e7400..caf3cf3 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -222,54 +222,6 @@
         return countSize;
     }
 
-    private static final int HEADER_READING_BUFFER_SIZE = 16384;
-    /**
-     * Convenience method to read the header of a binary file.
-     *
-     * This is quite resource intensive - don't call when performance is critical.
-     *
-     * @param file The file to read.
-     * @param offset The offset in the file where to start reading the data.
-     * @param length The length of the data file.
-     * @return the header of the specified dictionary file.
-     */
-    private static DictionaryHeader getDictionaryFileHeader(
-            final File file, final long offset, final long length)
-            throws FileNotFoundException, IOException, UnsupportedFormatException {
-        final byte[] buffer = new byte[HEADER_READING_BUFFER_SIZE];
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, offset, length,
-                new DictDecoder.DictionaryBufferFactory() {
-                    @Override
-                    public DictBuffer getDictionaryBuffer(File file)
-                            throws FileNotFoundException, IOException {
-                        final FileInputStream inStream = new FileInputStream(file);
-                        try {
-                            inStream.skip(offset);
-                            inStream.read(buffer);
-                            return new ByteArrayDictBuffer(buffer);
-                        } finally {
-                            inStream.close();
-                        }
-                    }
-                });
-        if (dictDecoder == null) {
-            return null;
-        }
-        return dictDecoder.readHeader();
-    }
-
-    public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file, final long offset,
-            final long length) {
-        try {
-            final DictionaryHeader header = getDictionaryFileHeader(file, offset, length);
-            return header;
-        } catch (UnsupportedFormatException e) {
-            return null;
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
     /**
      * Helper method to hide the actual value of the no children address.
      */
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 9abecbf..484bb4b 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -341,6 +341,7 @@
         return null;
     }
 
+    @UsedForTesting
     public static DictDecoder getDictDecoder(final File dictFile, final long offset,
             final long length, final DictionaryBufferFactory factory) {
         if (dictFile.isDirectory()) {
@@ -351,6 +352,7 @@
         return null;
     }
 
+    @UsedForTesting
     public static DictDecoder getDictDecoder(final File dictFile, final long offset,
             final long length) {
         return getDictDecoder(dictFile, offset, length, DictDecoder.USE_READONLY_BYTEBUFFER);
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index ae1e443..ab24fbc 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -123,6 +123,7 @@
     private final DictionaryBufferFactory mBufferFactory;
     protected DictBuffer mDictBuffer;
 
+    @UsedForTesting
     /* package */ Ver2DictDecoder(final File file, final long offset, final long length,
             final int factoryFlag) {
         mDictionaryBinaryFile = file;
diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
index 6872285..6388300 100644
--- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
@@ -17,8 +17,13 @@
 package com.android.inputmethod.latin.utils;
 
 import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.personalization.PersonalizationHelper;
 
+import java.io.File;
+import java.io.IOException;
 import java.util.Locale;
 import java.util.Map;
 
@@ -39,6 +44,26 @@
     private static native int editDistanceNative(int[] before, int[] after);
     private static native int setCurrentTimeForTestNative(int currentTime);
 
+    public static DictionaryHeader getHeader(final File dictFile)
+            throws IOException, UnsupportedFormatException {
+        return getHeaderWithOffsetAndLength(dictFile, 0 /* offset */, dictFile.length());
+    }
+
+    public static DictionaryHeader getHeaderWithOffsetAndLength(final File dictFile,
+            final long offset, final long length) throws IOException, UnsupportedFormatException {
+        // dictType is never used for reading the header. Passing an empty string.
+        final BinaryDictionary binaryDictionary = new BinaryDictionary(
+                dictFile.getAbsolutePath(), offset, length,
+                true /* useFullEditDistance */, null /* locale */, "" /* dictType */,
+                false /* isUpdatable */);
+        final DictionaryHeader header = binaryDictionary.getHeader();
+        binaryDictionary.close();
+        if (header == null) {
+            throw new IOException();
+        }
+        return header;
+    }
+
     public static boolean createEmptyDictFile(final String filePath, final long dictVersion,
             final Locale locale, final Map<String, String> attributeMap) {
         final String[] keyArray = new String[attributeMap.size()];
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index a155565..e531d4b 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -30,9 +30,11 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Locale;
@@ -283,7 +285,20 @@
     }
 
     public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file) {
-        return BinaryDictIOUtils.getDictionaryFileHeaderOrNull(file, 0, file.length());
+        return getDictionaryFileHeaderOrNull(file, 0, file.length());
+    }
+
+    private static DictionaryHeader getDictionaryFileHeaderOrNull(final File file,
+            final long offset, final long length) {
+        try {
+            final DictionaryHeader header =
+                    BinaryDictionaryUtils.getHeaderWithOffsetAndLength(file, offset, length);
+            return header;
+        } catch (UnsupportedFormatException e) {
+            return null;
+        } catch (IOException e) {
+            return null;
+        }
     }
 
     /**
@@ -294,7 +309,7 @@
      */
     private static DictionaryInfo createDictionaryInfoFromFileAddress(
             final AssetFileAddress fileAddress) {
-        final DictionaryHeader header = BinaryDictIOUtils.getDictionaryFileHeaderOrNull(
+        final DictionaryHeader header = getDictionaryFileHeaderOrNull(
                 new File(fileAddress.mFilename), fileAddress.mOffset, fileAddress.mLength);
         if (header == null) {
             return null;
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index 3b3da96..9657b9d 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -31,15 +31,12 @@
     -Wwrite-strings -Wfloat-equal -Wpointer-arith -Winit-self -Wredundant-decls \
     -Woverloaded-virtual -Wstrict-null-sentinel -Wsign-promo -Wno-system-headers
 
-ifeq ($(TARGET_ARCH), arm)
-ifeq ($(TARGET_GCC_VERSION), 4.6)
-LOCAL_CFLAGS += -Winline
-endif # TARGET_GCC_VERSION
-endif # TARGET_ARCH
-
 # To suppress compiler warnings for unused variables/functions used for debug features etc.
 LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
 
+# For C++11
+LOCAL_CFLAGS += -std=c++11
+
 include $(LOCAL_PATH)/NativeFileList.mk
 
 LOCAL_SRC_FILES := \
@@ -64,7 +61,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SDK_VERSION := 14
-LOCAL_NDK_STL_VARIANT := stlport_static
+LOCAL_NDK_STL_VARIANT := gnustl_static
 
 include $(BUILD_STATIC_LIBRARY)
 ######################################
@@ -87,7 +84,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SDK_VERSION := 14
-LOCAL_NDK_STL_VARIANT := stlport_static
+LOCAL_NDK_STL_VARIANT := gnustl_static
 LOCAL_LDFLAGS += -ldl
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index 82237dc..1f58246 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -32,7 +32,6 @@
         digraph_utils.cpp \
         error_type_utils.cpp \
         multi_bigram_map.cpp \
-        suggestions_output_utils.cpp \
         word_property.cpp) \
     $(addprefix suggest/core/layout/, \
         additional_proximity_chars.cpp \
@@ -41,6 +40,7 @@
         proximity_info_state.cpp \
         proximity_info_state_utils.cpp) \
     suggest/core/policy/weighting.cpp \
+    suggest/core/result/suggestions_output_utils.cpp \
     suggest/core/session/dic_traverse_session.cpp \
     $(addprefix suggest/policyimpl/dictionary/, \
         header/header_policy.cpp \
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 22cc4c0..0715fbd 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -352,13 +352,13 @@
 #define MIN_OUTPUT_INDEX_FOR_DEBUG (-1)
 
 #define DISALLOW_DEFAULT_CONSTRUCTOR(TypeName) \
-  TypeName()
+  TypeName() = delete
 
 #define DISALLOW_COPY_CONSTRUCTOR(TypeName) \
-  TypeName(const TypeName&)
+  TypeName(const TypeName&) = delete
 
 #define DISALLOW_ASSIGNMENT_OPERATOR(TypeName) \
-  void operator=(const TypeName&)
+  void operator=(const TypeName&) = delete
 
 #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
   DISALLOW_COPY_CONSTRUCTOR(TypeName);     \
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
index bc4ca8e..f846154 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
@@ -16,6 +16,7 @@
 
 #include "suggest/core/layout/proximity_info_state_utils.h"
 
+#include <algorithm>
 #include <cmath>
 #include <cstring> // for memset()
 #include <sstream> // for debug prints
diff --git a/native/jni/src/suggest/core/layout/proximity_info_utils.h b/native/jni/src/suggest/core/layout/proximity_info_utils.h
index bc8d5bc..6d2c11b 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_utils.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_utils.h
@@ -164,12 +164,15 @@
             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) {
-        if (x == NOT_A_COORDINATE || y == NOT_A_COORDINATE) {
-            return;
-        }
         const int mostCommonKeyWidthSquare = mostCommonKeyWidth * mostCommonKeyWidth;
         int insertPos = 0;
         proximities[insertPos++] = primaryKey;
+        if (x == NOT_A_COORDINATE || y == NOT_A_COORDINATE) {
+            for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
+                proximities[i] = NOT_A_CODE_POINT;
+            }
+            return;
+        }
         const int startIndex = getStartIndexFromCoordinates(x, y, cellHeight, cellWidth, gridWidth);
         if (startIndex >= 0) {
             for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
diff --git a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
similarity index 99%
rename from native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp
rename to native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index 07c2e6e..e9fb3b8 100644
--- a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "suggest/core/dictionary/suggestions_output_utils.h"
+#include "suggest/core/result/suggestions_output_utils.h"
 
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_utils.h"
diff --git a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h
similarity index 100%
rename from native/jni/src/suggest/core/dictionary/suggestions_output_utils.h
rename to native/jni/src/suggest/core/result/suggestions_output_utils.h
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 56acc2d..c3b6703 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -21,11 +21,11 @@
 #include "suggest/core/dicnode/dic_node_vector.h"
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/dictionary/digraph_utils.h"
-#include "suggest/core/dictionary/suggestions_output_utils.h"
 #include "suggest/core/layout/proximity_info.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "suggest/core/policy/traversal.h"
 #include "suggest/core/policy/weighting.h"
+#include "suggest/core/result/suggestions_output_utils.h"
 #include "suggest/core/session/dic_traverse_session.h"
 
 namespace latinime {
diff --git a/native/jni/src/utils/hash_map_compat.h b/native/jni/src/utils/hash_map_compat.h
index a1e982b..7bf35a6 100644
--- a/native/jni/src/utils/hash_map_compat.h
+++ b/native/jni/src/utils/hash_map_compat.h
@@ -17,18 +17,12 @@
 #ifndef LATINIME_HASH_MAP_COMPAT_H
 #define LATINIME_HASH_MAP_COMPAT_H
 
-// TODO: Use std::unordered_map that has been standardized in C++11
+#include <unordered_map>
 
-#ifdef __APPLE__
-#include <ext/hash_map>
-#else // __APPLE__
-#include <hash_map>
-#endif // __APPLE__
+#define hash_map_compat std::unordered_map
 
-#ifdef __SGI_STL_PORT
-#define hash_map_compat stlport::hash_map
-#else // __SGI_STL_PORT
-#define hash_map_compat __gnu_cxx::hash_map
-#endif // __SGI_STL_PORT
+#if 0 // TODO: Use this instead of the above macro.
+template <typename TKey, typename TValue> using hash_map_compat = std::unordered_map<TKey, TValue>;
+#endif
 
 #endif // LATINIME_HASH_MAP_COMPAT_H
diff --git a/tools/dicttool/NativeLib.mk b/tools/dicttool/NativeLib.mk
index 05e5841..26b6774 100644
--- a/tools/dicttool/NativeLib.mk
+++ b/tools/dicttool/NativeLib.mk
@@ -33,6 +33,10 @@
 LOCAL_CFLAGS += -DHOST_TOOL -fPIC -Wno-deprecated
 LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
 
+# For C++11
+# TODO: Change this to -std=c++11
+LOCAL_CFLAGS += -std=gnu++0x
+
 LATINIME_NATIVE_JNI_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni
 LATINIME_NATIVE_SRC_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni/src
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(LATINIME_NATIVE_SRC_DIR)