Merge "Move DictUpdater to the tests directory."
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index a269d49..f4d072d 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -87,6 +87,11 @@
     <!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=65] -->
     <string name="use_contacts_dict_summary">Use names from Contacts for suggestions and corrections</string>
 
+    <!-- Option name for enabling the use by the keyboards of sent/received messages, e-mail and typing history to improve suggestion accuracy [CHAR LIMIT=25] -->
+    <string name="use_personalized_dicts">Personalized suggestions</string>
+    <!-- Description for option enabling the use by the keyboards of sent/received messages, e-mail and typing history to improve suggestion accuracy [CHAR LIMIT=65] -->
+    <string name="use_personalized_dicts_summary">Learn from your communications and typed data to improve suggestions</string>
+
     <!-- Option name for enabling or disabling the double-space period feature that lets double tap on spacebar insert a period followed by a space [CHAR LIMIT=30] -->
     <string name="use_double_space_period">Double-space period</string>
     <!-- Description for option enabling or disabling the double-space period feature that lets double tap on spacebar insert a period followed by a space [CHAR LIMIT=65] -->
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index bf3b623..4f7c9f0 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -134,6 +134,12 @@
                 android:persistent="true"
                 android:defaultValue="true" />
             <CheckBoxPreference
+                android:key="pref_key_use_personalized_dicts"
+                android:title="@string/use_personalized_dicts"
+                android:summary="@string/use_personalized_dicts_summary"
+                android:persistent="true"
+                android:defaultValue="true" />
+            <CheckBoxPreference
                 android:key="pref_key_use_double_space_period"
                 android:title="@string/use_double_space_period"
                 android:summary="@string/use_double_space_period_summary"
diff --git a/java/res/xml/rowkeys_armenian_phonetic2.xml b/java/res/xml/rowkeys_armenian_phonetic2.xml
index 5dcabc3..3764d0d 100644
--- a/java/res/xml/rowkeys_armenian_phonetic2.xml
+++ b/java/res/xml/rowkeys_armenian_phonetic2.xml
@@ -34,6 +34,7 @@
     <Key
         latin:keyLabel="&#x0565;"
         latin:moreKeys="&#x0587;"
+        latin:keyHintLabel="&#x0587;"
         latin:keyLabelFlags="fontNormal" />
     <!-- U+057C: "ռ" ARMENIAN SMALL LETTER RA -->
     <Key
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index fb84f1d..53b4485 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -187,7 +187,7 @@
         final MainKeyboardView keyboardView = mKeyboardView;
         final Keyboard oldKeyboard = keyboardView.getKeyboard();
         keyboardView.setKeyboard(keyboard);
-        mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
+        mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding);
         keyboardView.setKeyPreviewPopupEnabled(
                 Settings.readKeyPreviewPopupEnabled(mPrefs, mResources),
                 Settings.readKeyPreviewPopupDismissDelay(mPrefs, mResources));
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 1400e05..4b6e12f 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -932,11 +932,7 @@
     @Override
     public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
         locatePreviewPlacerView();
-        // TODO: Remove this check
-        if (panel.isShowingInParent()) {
-            panel.dismissMoreKeysPanel();
-        }
-        mPreviewPlacerView.addView(panel.getContainerView());
+        panel.showInParent(mPreviewPlacerView);
         mMoreKeysPanel = panel;
         dimEntireKeyboard(true /* dimmed */);
     }
@@ -954,7 +950,7 @@
     public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {
         dimEntireKeyboard(false /* dimmed */);
         if (isShowingMoreKeysPanel()) {
-            mPreviewPlacerView.removeView(mMoreKeysPanel.getContainerView());
+            mMoreKeysPanel.removeFromParent();
             mMoreKeysPanel = null;
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index a7c4685..5b13e9a 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -21,6 +21,7 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
@@ -216,12 +217,26 @@
         return true;
     }
 
-    @Override
-    public View getContainerView() {
+    private View getContainerView() {
         return (View)getParent();
     }
 
     @Override
+    public void showInParent(final ViewGroup parentView) {
+        removeFromParent();
+        parentView.addView(getContainerView());
+    }
+
+    @Override
+    public void removeFromParent() {
+        final View containerView = getContainerView();
+        final ViewGroup currentParent = (ViewGroup)containerView.getParent();
+        if (currentParent != null) {
+            currentParent.removeView(containerView);
+        }
+    }
+
+    @Override
     public boolean isShowingInParent() {
         return (getContainerView().getParent() != null);
     }
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
index 886c628..4a33e65 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard;
 
 import android.view.View;
+import android.view.ViewGroup;
 
 public interface MoreKeysPanel {
     public interface Controller {
@@ -119,9 +120,16 @@
     public int translateY(int y);
 
     /**
-     * Return the view containing the more keys panel.
+     * Show this {@link MoreKeysPanel} in the parent view.
+     *
+     * @param parentView the {@link ViewGroup} that hosts this {@link MoreKeysPanel}.
      */
-    public View getContainerView();
+    public void showInParent(ViewGroup parentView);
+
+    /**
+     * Remove this {@link MoreKeysPanel} from the parent view.
+     */
+    public void removeFromParent();
 
     /**
      * Return whether the panel is currently being shown.
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index a2528e0..e23d5a7 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -167,10 +167,10 @@
     private static boolean sInGesture = false;
     private static long sGestureFirstDownTime;
     private static TimeRecorder sTimeRecorder;
-    private static final InputPointers sAggregratedPointers = new InputPointers(
+    private static final InputPointers sAggregatedPointers = new InputPointers(
             GestureStroke.DEFAULT_CAPACITY);
-    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
-    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
+    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregatedPointers
+    private static long sLastRecognitionTime = 0; // synchronized using sAggregatedPointers
 
     static final class BogusMoveEventDetector {
         // Move these thresholds to resource.
@@ -737,8 +737,8 @@
             Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
         }
         sInGesture = true;
-        synchronized (sAggregratedPointers) {
-            sAggregratedPointers.reset();
+        synchronized (sAggregatedPointers) {
+            sAggregatedPointers.reset();
             sLastRecognitionPointSize = 0;
             sLastRecognitionTime = 0;
             sListener.onStartBatchInput();
@@ -769,10 +769,10 @@
     }
 
     private void updateBatchInput(final long eventTime) {
-        synchronized (sAggregratedPointers) {
+        synchronized (sAggregatedPointers) {
             final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
-            stroke.appendIncrementalBatchPoints(sAggregratedPointers);
-            final int size = sAggregratedPointers.getPointerSize();
+            stroke.appendIncrementalBatchPoints(sAggregatedPointers);
+            final int size = sAggregatedPointers.getPointerSize();
             if (size > sLastRecognitionPointSize
                     && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
                 if (DEBUG_LISTENER) {
@@ -780,18 +780,18 @@
                             size));
                 }
                 sTimerProxy.startUpdateBatchInputTimer(this);
-                sListener.onUpdateBatchInput(sAggregratedPointers);
+                sListener.onUpdateBatchInput(sAggregatedPointers);
                 // The listener may change the size of the pointers (when auto-committing
                 // for example), so we need to get the size from the pointers again.
-                sLastRecognitionPointSize = sAggregratedPointers.getPointerSize();
+                sLastRecognitionPointSize = sAggregatedPointers.getPointerSize();
                 sLastRecognitionTime = eventTime;
             }
         }
     }
 
     private void mayEndBatchInput(final long eventTime) {
-        synchronized (sAggregratedPointers) {
-            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
+        synchronized (sAggregatedPointers) {
+            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregatedPointers);
             if (getActivePointerTrackerCount() == 1) {
                 sInGesture = false;
                 sTimeRecorder.onEndBatchInput(eventTime);
@@ -799,9 +799,9 @@
                 if (!mIsTrackingForActionDisabled) {
                     if (DEBUG_LISTENER) {
                         Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
-                                mPointerId, sAggregratedPointers.getPointerSize()));
+                                mPointerId, sAggregatedPointers.getPointerSize()));
                     }
-                    sListener.onEndBatchInput(sAggregratedPointers);
+                    sListener.onEndBatchInput(sAggregatedPointers);
                 }
             }
         }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index f708c2c..9f33fcc 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -1811,7 +1811,7 @@
         // U+055A: "՚" ARMENIAN APOSTROPHE
         // U+055B: "՛" ARMENIAN EMPHASIS MARK
         // U+055F: "՟" ARMENIAN ABBREVIATION MARK
-        /* 59 */ "!fixedColumnOrder!8,!,?,\\,,.,\u058A,\u055C,\u055D,\u055E,:,;,@,\u0559,\u055A,\u055B,\u055F",
+        /* 59 */ "!fixedColumnOrder!8,!,?,\u0559,\u055A,.,\u055C,\\,,\u055E,:,;,\u055F,\u00AB,\u00BB,\u058A,\u055D,\u055B",
         /* 60~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index 81ccf83..76b0912 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -23,87 +23,203 @@
 import android.view.View;
 import android.widget.LinearLayout;
 
-public final class InputView extends LinearLayout {
-    private View mSuggestionStripView;
-    private View mKeyboardView;
-    private int mKeyboardTopPadding;
+import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.suggestions.MoreSuggestionsView;
+import com.android.inputmethod.latin.suggestions.SuggestionStripView;
 
-    private boolean mIsForwardingEvent;
+public final class InputView extends LinearLayout {
     private final Rect mInputViewRect = new Rect();
-    private final Rect mEventForwardingRect = new Rect();
-    private final Rect mEventReceivingRect = new Rect();
+    private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
+    private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
 
     public InputView(final Context context, final AttributeSet attrs) {
         super(context, attrs, 0);
     }
 
-    public void setKeyboardGeometry(final int keyboardTopPadding) {
-        mKeyboardTopPadding = keyboardTopPadding;
-    }
-
     @Override
     protected void onFinishInflate() {
-        mSuggestionStripView = findViewById(R.id.suggestion_strip_view);
-        mKeyboardView = findViewById(R.id.keyboard_view);
+        final SuggestionStripView suggestionStripView =
+                (SuggestionStripView)findViewById(R.id.suggestion_strip_view);
+        final MainKeyboardView mainKeyboardView =
+                (MainKeyboardView)findViewById(R.id.keyboard_view);
+        mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder(
+                mainKeyboardView, suggestionStripView);
+        mMoreSuggestionsViewCanceler = new MoreSuggestionsViewCanceler(
+                mainKeyboardView, suggestionStripView);
+    }
+
+    public void setKeyboardTopPadding(final int keyboardTopPadding) {
+        mKeyboardTopPaddingForwarder.setKeyboardTopPadding(keyboardTopPadding);
     }
 
     @Override
     public boolean dispatchTouchEvent(final MotionEvent me) {
-        if (mSuggestionStripView.getVisibility() != VISIBLE
-                || mKeyboardView.getVisibility() != VISIBLE) {
-            return super.dispatchTouchEvent(me);
-        }
-
-        // The touch events that hit the top padding of keyboard should be forwarded to
-        // {@link SuggestionStripView}.
         final Rect rect = mInputViewRect;
-        this.getGlobalVisibleRect(rect);
+        getGlobalVisibleRect(rect);
         final int x = (int)me.getX() + rect.left;
         final int y = (int)me.getY() + rect.top;
 
-        final Rect forwardingRect = mEventForwardingRect;
-        mKeyboardView.getGlobalVisibleRect(forwardingRect);
-        if (!mIsForwardingEvent && !forwardingRect.contains(x, y)) {
-            return super.dispatchTouchEvent(me);
+        // The touch events that hit the top padding of keyboard should be
+        // forwarded to {@link SuggestionStripView}.
+        if (mKeyboardTopPaddingForwarder.dispatchTouchEvent(x, y, me)) {
+            return true;
+        }
+        // To cancel {@link MoreSuggestionsView}, we should intercept a touch event to
+        // {@link MainKeyboardView} and dismiss the {@link MoreSuggestionsView}.
+        if (mMoreSuggestionsViewCanceler.dispatchTouchEvent(x, y, me)) {
+            return true;
+        }
+        return super.dispatchTouchEvent(me);
+    }
+
+    /**
+     * This class forwards series of {@link MotionEvent}s from <code>Forwarder</code> view to
+     * <code>Receiver</code> view.
+     *
+     * @param <Sender> a {@link View} that may send a {@link MotionEvent} to <Receiver>.
+     * @param <Receiver> a {@link View} that receives forwarded {@link MotionEvent} from
+     *     <Forwarder>.
+     */
+    private static abstract class MotionEventForwarder<Sender extends View, Receiver extends View> {
+        protected final Sender mSenderView;
+        protected final Receiver mReceiverView;
+
+        private boolean mIsForwardingEvent;
+        protected final Rect mEventSendingRect = new Rect();
+        protected final Rect mEventReceivingRect = new Rect();
+
+        public MotionEventForwarder(final Sender senderView, final Receiver receiverView) {
+            mSenderView = senderView;
+            mReceiverView = receiverView;
         }
 
-        final int forwardingLimitY = forwardingRect.top + mKeyboardTopPadding;
-        boolean sendToTarget = false;
+        // Return true if a touch event of global coordinate x, y needs to be forwarded.
+        protected abstract boolean needsToForward(final int x, final int y);
 
-        switch (me.getAction()) {
-        case MotionEvent.ACTION_DOWN:
-            if (y < forwardingLimitY) {
-                // This down event and further move and up events should be forwarded to the target.
-                mIsForwardingEvent = true;
-                sendToTarget = true;
+        // Translate global x-coordinate to <code>Receiver</code> local coordinate.
+        protected int translateX(final int x) {
+            return x - mEventReceivingRect.left;
+        }
+
+        // Translate global y-coordinate to <code>Receiver</code> local coordinate.
+        protected int translateY(final int y) {
+            return y - mEventReceivingRect.top;
+        }
+
+        // Callback when a {@link MotionEvent} is forwarded.
+        protected void onForwardingEvent(final MotionEvent me) {}
+
+        // Dispatches a {@link MotioneEvent} to <code>Receiver</code> if needed and returns true.
+        // Otherwise returns false.
+        public boolean dispatchTouchEvent(final int x, final int y, final MotionEvent me) {
+            // Forwards a {link MotionEvent} only if both <code>Sender</code> and
+            // <code>Receiver</code> are visible.
+            if (mSenderView.getVisibility() != View.VISIBLE ||
+                    mReceiverView.getVisibility() != View.VISIBLE) {
+                return false;
             }
-            break;
-        case MotionEvent.ACTION_MOVE:
-            sendToTarget = mIsForwardingEvent;
-            break;
-        case MotionEvent.ACTION_UP:
-        case MotionEvent.ACTION_CANCEL:
-            sendToTarget = mIsForwardingEvent;
-            mIsForwardingEvent = false;
-            break;
+            final Rect sendingRect = mEventSendingRect;
+            mSenderView.getGlobalVisibleRect(sendingRect);
+            if (!mIsForwardingEvent && !sendingRect.contains(x, y)) {
+                return false;
+            }
+
+            boolean shouldForwardToReceiver = false;
+
+            switch (me.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                // If the down event happens in the forwarding area, successive {@link MotionEvent}s
+                // should be forwarded.
+                if (needsToForward(x, y)) {
+                    mIsForwardingEvent = true;
+                    shouldForwardToReceiver = true;
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                shouldForwardToReceiver = mIsForwardingEvent;
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                shouldForwardToReceiver = mIsForwardingEvent;
+                mIsForwardingEvent = false;
+                break;
+            }
+
+            if (!shouldForwardToReceiver) {
+                return false;
+            }
+
+            final Rect receivingRect = mEventReceivingRect;
+            mReceiverView.getGlobalVisibleRect(receivingRect);
+            // Translate global coordinates to <code>Receiver</code> local coordinates.
+            me.setLocation(translateX(x), translateY(y));
+            mReceiverView.dispatchTouchEvent(me);
+            onForwardingEvent(me);
+            return true;
+        }
+    }
+
+    /**
+     * This class forwards {@link MotionEvent}s happened in the top padding of
+     * {@link MainKeyboardView} to {@link SuggestionStripView}.
+     */
+    private static class KeyboardTopPaddingForwarder
+            extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {
+        private int mKeyboardTopPadding;
+
+        public KeyboardTopPaddingForwarder(final MainKeyboardView mainKeyboardView,
+                final SuggestionStripView suggestionStripView) {
+            super(mainKeyboardView, suggestionStripView);
         }
 
-        if (!sendToTarget) {
-            return super.dispatchTouchEvent(me);
+        public void setKeyboardTopPadding(final int keyboardTopPadding) {
+            mKeyboardTopPadding = keyboardTopPadding;
         }
 
-        final Rect receivingRect = mEventReceivingRect;
-        mSuggestionStripView.getGlobalVisibleRect(receivingRect);
-        final int translatedX = x - receivingRect.left;
-        final int translatedY;
-        if (y < forwardingLimitY) {
-            // The forwarded event should have coordinates that are inside of the target.
-            translatedY = Math.min(y - receivingRect.top, receivingRect.height() - 1);
-        } else {
-            translatedY = y - receivingRect.top;
+        private boolean isInKeyboardTopPadding(final int y) {
+            return y < mEventSendingRect.top + mKeyboardTopPadding;
         }
-        me.setLocation(translatedX, translatedY);
-        mSuggestionStripView.dispatchTouchEvent(me);
-        return true;
+
+        @Override
+        protected boolean needsToForward(final int x, final int y) {
+            return isInKeyboardTopPadding(y);
+        }
+
+        @Override
+        protected int translateY(final int y) {
+            final int translatedY = super.translateY(y);
+            if (isInKeyboardTopPadding(y)) {
+                // The forwarded event should have coordinates that are inside of
+                // the target.
+                return Math.min(translatedY, mEventReceivingRect.height() - 1);
+            }
+            return translatedY;
+        }
+    }
+
+    /**
+     * This class forwards {@link MotionEvent}s happened in the {@link MainKeyboardView} to
+     * {@link SuggestionStripView} when the {@link MoreSuggestionsView} is showing.
+     * {@link SuggestionStripView} dismisses {@link MoreSuggestionsView} when it receives those
+     * events.
+     */
+    private static class MoreSuggestionsViewCanceler
+            extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {
+        public MoreSuggestionsViewCanceler(final MainKeyboardView mainKeyboardView,
+                final SuggestionStripView suggestionStripView) {
+            super(mainKeyboardView, suggestionStripView);
+        }
+
+        @Override
+        protected boolean needsToForward(final int x, final int y) {
+            return mReceiverView.isShowingMoreSuggestionPanel() && mEventSendingRect.contains(x, y);
+        }
+
+        @Override
+        protected void onForwardingEvent(final MotionEvent me) {
+            if (me.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mReceiverView.dismissMoreSuggestionsPanel();
+            }
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 8b46655..72d5435 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1469,7 +1469,7 @@
                 ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert,
                         false /* isBatchMode */);
             }
-            mWordComposer.doubleSpacePeriod();
+            mWordComposer.discardPreviousWordForSuggestion();
             mKeyboardSwitcher.updateShiftState();
             return true;
         }
@@ -2565,29 +2565,23 @@
         final SettingsValues currentSettings = mSettings.getCurrent();
         final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues;
 
-        final String previousWord;
-        if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) {
-            previousWord = mWordComposer.getPreviousWord();
-        } else {
-            // Not composing: this is for prediction.
-            // TODO: read the previous word earlier for prediction, like we are doing for
-            // normal suggestions.
-            previousWord = getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord*/);
-        }
         if (DEBUG) {
-            // TODO: this is for checking consistency with older versions. Remove this when
-            // we are confident this is stable.
-            // We're checking the previous word in the text field against the memorized previous
-            // word. If we are composing a word we should have the second word before the cursor
-            // memorized, otherwise we should have the first.
-            final String rereadPrevWord = getNthPreviousWordForSuggestion(currentSettings,
-                    mWordComposer.isComposingWord() ? 2 : 1);
-            if (!TextUtils.equals(previousWord, rereadPrevWord)) {
-                throw new RuntimeException("Unexpected previous word: "
-                        + previousWord + " <> " + rereadPrevWord);
+            if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) {
+                final String previousWord = mWordComposer.getPreviousWordForSuggestion();
+                // TODO: this is for checking consistency with older versions. Remove this when
+                // we are confident this is stable.
+                // We're checking the previous word in the text field against the memorized previous
+                // word. If we are composing a word we should have the second word before the cursor
+                // memorized, otherwise we should have the first.
+                final String rereadPrevWord = getNthPreviousWordForSuggestion(currentSettings,
+                        mWordComposer.isComposingWord() ? 2 : 1);
+                if (!TextUtils.equals(previousWord, rereadPrevWord)) {
+                    throw new RuntimeException("Unexpected previous word: "
+                            + previousWord + " <> " + rereadPrevWord);
+                }
             }
         }
-        suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWord(),
+        suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWordForSuggestion(),
                 keyboard.getProximityInfo(),
                 currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
                 additionalFeaturesOptions, sessionId, sequenceNumber, callback);
@@ -2830,6 +2824,19 @@
         // strings.
         mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord, separatorString,
                 prevWord);
+        final boolean shouldDiscardPreviousWordForSuggestion;
+        if (0 == StringUtils.codePointCount(separatorString)) {
+            // Separator is 0-length. Discard the word only if the current language has spaces.
+            shouldDiscardPreviousWordForSuggestion =
+                    mSettings.getCurrent().mCurrentLanguageHasSpaces;
+        } else {
+            // Otherwise, we discard if the separator contains any non-whitespace.
+            shouldDiscardPreviousWordForSuggestion =
+                    !StringUtils.containsOnlyWhitespace(separatorString);
+        }
+        if (shouldDiscardPreviousWordForSuggestion) {
+            mWordComposer.discardPreviousWordForSuggestion();
+        }
     }
 
     private void setPunctuationSuggestions() {
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 5ecfc67..7da97e5 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -50,8 +50,9 @@
     private final StringBuilder mTypedWord;
     // The previous word (before the composing word). Used as context for suggestions. May be null
     // after resetting and before starting a new composing word, or when there is no context like
-    // at the start of text for example.
-    private String mPreviousWord;
+    // at the start of text for example. It can also be set to null externally when the user
+    // enters a separator that does not let bigrams across, like a period or a comma.
+    private String mPreviousWordForSuggestion;
     private String mAutoCorrection;
     private boolean mIsResumed;
     private boolean mIsBatchMode;
@@ -89,7 +90,7 @@
         mIsBatchMode = false;
         mCursorPositionWithinWord = 0;
         mRejectedBatchModeSuggestion = null;
-        mPreviousWord = null;
+        mPreviousWordForSuggestion = null;
         refreshSize();
     }
 
@@ -106,7 +107,7 @@
         mIsBatchMode = source.mIsBatchMode;
         mCursorPositionWithinWord = source.mCursorPositionWithinWord;
         mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion;
-        mPreviousWord = source.mPreviousWord;
+        mPreviousWordForSuggestion = source.mPreviousWordForSuggestion;
         refreshSize();
     }
 
@@ -124,7 +125,7 @@
         mIsBatchMode = false;
         mCursorPositionWithinWord = 0;
         mRejectedBatchModeSuggestion = null;
-        mPreviousWord = null;
+        mPreviousWordForSuggestion = null;
         refreshSize();
     }
 
@@ -305,7 +306,7 @@
             addKeyInfo(codePoint, keyboard);
         }
         mIsResumed = true;
-        mPreviousWord = previousWord;
+        mPreviousWordForSuggestion = previousWord;
     }
 
     /**
@@ -356,8 +357,8 @@
         return mTypedWord.toString();
     }
 
-    public String getPreviousWord() {
-        return mPreviousWord;
+    public String getPreviousWordForSuggestion() {
+        return mPreviousWordForSuggestion;
     }
 
     /**
@@ -419,7 +420,7 @@
     public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
             final String previousWord) {
         mCapitalizedMode = mode;
-        mPreviousWord = previousWord;
+        mPreviousWordForSuggestion = previousWord;
     }
 
     /**
@@ -471,12 +472,7 @@
         mCapsCount = 0;
         mDigitsCount = 0;
         mIsBatchMode = false;
-        final boolean isWhitespace = 1 == StringUtils.codePointCount(separatorString)
-                && Character.isWhitespace(separatorString.codePointAt(0));
-        // If not whitespace, we don't use the previous word for suggestion. This is consistent
-        // with how we get the previous word for suggestion: see RichInputConnection#spaceRegex and
-        // LatinIME#getNthPreviousWordForSuggestion.
-        mPreviousWord = isWhitespace ? mTypedWord.toString() : null;
+        mPreviousWordForSuggestion = mTypedWord.toString();
         mTypedWord.setLength(0);
         mCodePointSize = 0;
         mTrailingSingleQuotesCount = 0;
@@ -490,11 +486,11 @@
         return lastComposedWord;
     }
 
-    public void doubleSpacePeriod() {
-        // When a period was entered with a double space, the separator we got has been
-        // changed by a period (see #commitWord). We should not use the previous word for
-        // suggestion.
-        mPreviousWord = null;
+    // Call this when the recorded previous word should be discarded. This is typically called
+    // when the user inputs a separator that's not whitespace (including the case of the
+    // double-space-to-period feature).
+    public void discardPreviousWordForSuggestion() {
+        mPreviousWordForSuggestion = null;
     }
 
     public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
@@ -509,7 +505,7 @@
         mCursorPositionWithinWord = mCodePointSize;
         mRejectedBatchModeSuggestion = null;
         mIsResumed = true;
-        mPreviousWord = previousWord;
+        mPreviousWordForSuggestion = previousWord;
     }
 
     public boolean isBatchMode() {
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 4d1abfa..2dbb5eb 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -439,7 +439,7 @@
             final FormatOptions options) {
         dictDecoder.setPosition(headerSize);
         final int count = dictDecoder.readPtNodeCount();
-        int groupPos = headerSize + BinaryDictIOUtils.getPtNodeCountSize(count);
+        int groupPos = dictDecoder.getPosition();
         final StringBuilder builder = new StringBuilder();
         WeightedString result = null;
 
@@ -501,9 +501,9 @@
         do { // Scan the linked-list node.
             final int nodeArrayHeadPos = dictDecoder.getPosition();
             final int count = dictDecoder.readPtNodeCount();
-            int groupOffsetPos = nodeArrayHeadPos + BinaryDictIOUtils.getPtNodeCountSize(count);
+            int groupPos = dictDecoder.getPosition();
             for (int i = count; i > 0; --i) { // Scan the array of PtNode.
-                PtNodeInfo info = dictDecoder.readPtNode(groupOffsetPos, options);
+                PtNodeInfo info = dictDecoder.readPtNode(groupPos, options);
                 if (BinaryDictIOUtils.isMovedPtNode(info.mFlags, options)) continue;
                 ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
                 ArrayList<WeightedString> bigrams = null;
@@ -539,7 +539,7 @@
                                     0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
                                     0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED)));
                 }
-                groupOffsetPos = info.mEndAddress;
+                groupPos = info.mEndAddress;
             }
 
             // reach the end of the array.
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 7a7d452..07ba777 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -85,7 +85,7 @@
 
             if (p.mNumOfPtNode == Position.NOT_READ_PTNODE_COUNT) {
                 p.mNumOfPtNode = dictDecoder.readPtNodeCount();
-                p.mAddress += getPtNodeCountSize(p.mNumOfPtNode);
+                p.mAddress = dictDecoder.getPosition();
                 p.mPosition = 0;
             }
             if (p.mNumOfPtNode == 0) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index af54805..437fa94 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -188,7 +188,7 @@
     // us to change the format during development while having testing devices remove
     // older files with each upgrade, while still having a readable versioning scheme.
     public static final int VERSION2 = 2;
-    public static final int VERSION4 = 400;
+    public static final int VERSION4 = 401;
     static final int MINIMUM_SUPPORTED_VERSION = VERSION2;
     static final int MAXIMUM_SUPPORTED_VERSION = VERSION4;
 
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 714c3a9..94f1458 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -53,6 +53,7 @@
     public static final String PREF_MISC_SETTINGS = "misc_settings";
     public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
     public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
+    public static final String PREF_KEY_USE_PERSONALIZED_DICTS = "pref_key_use_personalized_dicts";
     public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
             "pref_key_use_double_space_period";
     public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE =
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 06406c1..d98e547 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -29,11 +29,9 @@
 import com.android.inputmethod.latin.InputAttributes;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.InputTypeUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
 
 import java.util.ArrayList;
@@ -71,6 +69,7 @@
     public final boolean mIncludesOtherImesInLanguageSwitchList;
     public final boolean mShowsLanguageSwitchKey;
     public final boolean mUseContactsDict;
+    public final boolean mUsePersonalizedDicts;
     public final boolean mUseDoubleSpacePeriod;
     public final boolean mBlockPotentiallyOffensive;
     // Use bigrams to predict the next word when there is no input for it yet
@@ -148,6 +147,7 @@
                 Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false);
         mShowsLanguageSwitchKey = Settings.readShowsLanguageSwitchKey(prefs);
         mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
+        mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true);
         mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true);
         mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res);
         mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res);
@@ -206,6 +206,7 @@
         mIncludesOtherImesInLanguageSwitchList = false;
         mShowsLanguageSwitchKey = true;
         mUseContactsDict = true;
+        mUsePersonalizedDicts = true;
         mUseDoubleSpacePeriod = true;
         mBlockPotentiallyOffensive = true;
         mAutoCorrectEnabled = true;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index aa87aff..073148a 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -162,19 +162,19 @@
         mSuggestionsStrip.removeAllViews();
         removeAllViews();
         addView(mSuggestionsStrip);
-        mMoreSuggestionsView.dismissMoreKeysPanel();
+        dismissMoreSuggestionsPanel();
     }
 
     private final MoreSuggestionsListener mMoreSuggestionsListener = new MoreSuggestionsListener() {
         @Override
         public void onSuggestionSelected(final int index, final SuggestedWordInfo wordInfo) {
             mListener.pickSuggestionManually(index, wordInfo);
-            mMoreSuggestionsView.dismissMoreKeysPanel();
+            dismissMoreSuggestionsPanel();
         }
 
         @Override
         public void onCancelInput() {
-            mMoreSuggestionsView.dismissMoreKeysPanel();
+            dismissMoreSuggestionsPanel();
         }
     };
 
@@ -192,10 +192,18 @@
 
         @Override
         public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {
-            mMoreSuggestionsView.dismissMoreKeysPanel();
+            dismissMoreSuggestionsPanel();
         }
     };
 
+    public boolean isShowingMoreSuggestionPanel() {
+        return mMoreSuggestionsView.isShowingInParent();
+    }
+
+    public void dismissMoreSuggestionsPanel() {
+        mMoreSuggestionsView.dismissMoreKeysPanel();
+    }
+
     @Override
     public boolean onLongClick(final View view) {
         AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
@@ -322,6 +330,6 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mMoreSuggestionsView.dismissMoreKeysPanel();
+        dismissMoreSuggestionsPanel();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index df42041..85f4454 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -250,6 +250,24 @@
         return true;
     }
 
+    /**
+     * Returns true if all code points in text are whitespace, false otherwise. Empty is true.
+     */
+    // Interestingly enough, U+00A0 NO-BREAK SPACE and U+200B ZERO-WIDTH SPACE are not considered
+    // whitespace, while EN SPACE, EM SPACE and IDEOGRAPHIC SPACES are.
+    public static boolean containsOnlyWhitespace(final String text) {
+        final int length = text.length();
+        int i = 0;
+        while (i < length) {
+            final int codePoint = text.codePointAt(i);
+            if (!Character.isWhitespace(codePoint)) {
+                return false;
+            }
+            i += Character.charCount(codePoint);
+        }
+        return true;
+    }
+
     @UsedForTesting
     public static boolean looksValidForDictionaryInsertion(final CharSequence text,
             final SettingsValues settings) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
index 9afb5f2..deed010 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
@@ -48,9 +48,9 @@
 const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 1;
 const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 1;
 
-const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4;
+const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 16;
 const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE = 4;
-const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 16;
+const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64;
 const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE = 4;
 
 const int Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE = 3;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
index eb2227d..7c6a21d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
@@ -31,7 +31,7 @@
     enum FORMAT_VERSION {
         // These MUST have the same values as the relevant constants in FormatSpec.java.
         VERSION_2 = 2,
-        VERSION_4 = 400,
+        VERSION_4 = 401,
         UNKNOWN_VERSION = -1
     };
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.cpp
index 4ad82f9..c380429 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.cpp
@@ -34,7 +34,8 @@
     const int indexTableReadingPos = getPosInIndexTable(id);
     const int index = mIndexTableBuffer->readUint(INDEX_SIZE, indexTableReadingPos);
     const int contentTableReadingPos = getPosInContentTable(id, index);
-    return mContentTableBuffer->readUint(mDataSize, contentTableReadingPos);
+    const int contentValue = mContentTableBuffer->readUint(mDataSize, contentTableReadingPos);
+    return contentValue == NOT_EXIST ? NOT_A_DICT_POS : contentValue;
 }
 
 bool SparseTable::set(const int id, const uint32_t value) {
@@ -70,7 +71,7 @@
     // Write a new block that containing the entry to be set.
     int writingPos = getPosInContentTable(0 /* id */, index);
     for (int i = 0; i < mBlockSize; ++i) {
-        if (!mContentTableBuffer->writeUintAndAdvancePosition(NOT_A_DICT_POS, mDataSize,
+        if (!mContentTableBuffer->writeUintAndAdvancePosition(NOT_EXIST, mDataSize,
                 &writingPos)) {
             AKLOGE("cannot write content table to extend. writingPos: %d, tailPos: %d, "
                     "mDataSize: %d", writingPos, mContentTableBuffer->getTailPosition(), mDataSize);
diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
index a67f6a4..1336c6d 100644
--- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java
+++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
@@ -51,14 +51,14 @@
         assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
         assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
         // Check the previous word is still there
-        assertEquals(PREVWORD, wc.getPreviousWord());
+        assertEquals(PREVWORD, wc.getPreviousWordForSuggestion());
         // Move the cursor past the end of the word
         assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1));
         assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15));
         // Do what LatinIME does when the cursor is moved outside of the word,
         // and check the behavior is correct.
         wc.reset();
-        assertNull(wc.getPreviousWord());
+        assertNull(wc.getPreviousWordForSuggestion());
 
         // \uD861\uDED7 is 𨛗, a character outside the BMP
         final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh";
@@ -73,35 +73,35 @@
         assertTrue(wc.isCursorFrontOrMiddleOfComposingWord());
         assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
         assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
-        assertNull(wc.getPreviousWord());
+        assertNull(wc.getPreviousWordForSuggestion());
 
         wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */);
         wc.setCursorPositionWithinWord(3);
         assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
-        assertEquals(STR_WITHIN_BMP, wc.getPreviousWord());
+        assertEquals(STR_WITHIN_BMP, wc.getPreviousWordForSuggestion());
 
         wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR,
                 null /* keyboard */);
         wc.setCursorPositionWithinWord(3);
         assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
-        assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord());
+        assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWordForSuggestion());
 
         wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */);
         wc.setCursorPositionWithinWord(3);
         assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3));
         assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1));
-        assertEquals(STR_WITHIN_BMP, wc.getPreviousWord());
+        assertEquals(STR_WITHIN_BMP, wc.getPreviousWordForSuggestion());
 
         wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */,
                 null /* keyboard */);
         wc.setCursorPositionWithinWord(3);
         assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9));
-        assertNull(wc.getPreviousWord());
+        assertNull(wc.getPreviousWordForSuggestion());
 
         wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR,
                 null /* keyboard */);
         assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10));
-        assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord());
+        assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWordForSuggestion());
 
         wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */,
                 null /* keyboard */);
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
index 2123e84..0c88f34 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -292,6 +292,20 @@
         assertTrue(bytesStr.equals(bytesStr2));
     }
 
+    public void testContainsOnlyWhitespace() {
+        assertTrue(StringUtils.containsOnlyWhitespace("   "));
+        assertTrue(StringUtils.containsOnlyWhitespace(""));
+        assertTrue(StringUtils.containsOnlyWhitespace("  \n\t\t"));
+        // U+2002 : EN SPACE
+        // U+2003 : EM SPACE
+        // U+3000 : IDEOGRAPHIC SPACE (commonly "double-width space")
+        assertTrue(StringUtils.containsOnlyWhitespace("\u2002\u2003\u3000"));
+        assertFalse(StringUtils.containsOnlyWhitespace("  a "));
+        assertFalse(StringUtils.containsOnlyWhitespace(". "));
+        assertFalse(StringUtils.containsOnlyWhitespace("."));
+        assertTrue(StringUtils.containsOnlyWhitespace(""));
+    }
+
     public void testJsonUtils() {
         final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 };
         final List<Object> objArray = Arrays.asList(objs);
diff --git a/tools/make-keyboard-text/res/values-hy-rAM/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hy-rAM/donottranslate-more-keys.xml
index a94d50e..73fe927 100644
--- a/tools/make-keyboard-text/res/values-hy-rAM/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-hy-rAM/donottranslate-more-keys.xml
@@ -26,7 +26,7 @@
     <!-- U+055A: "՚" ARMENIAN APOSTROPHE -->
     <!-- U+055B: "՛" ARMENIAN EMPHASIS MARK -->
     <!-- U+055F: "՟" ARMENIAN ABBREVIATION MARK -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,!,?,\\,,.,&#x058A;,&#x055C;,&#x055D;,&#x055E;,:,;,\@,&#x0559;,&#x055A;,&#x055B;,&#x055F;"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,!,?,&#x0559;,&#x055A;,.,&#x055C;,\\,,&#x055E;,:,;,&#x055F;,&#x00AB;,&#x00BB;,&#x058A;,&#x055D;,&#x055B;"</string>
     <!-- U+055E: "՞" ARMENIAN QUESTION MARK -->
     <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
     <string name="more_keys_for_question">&#x055E;,&#x00BF;</string>