am 11f72cd4: (-s ours) am 3f4d63e5: Merge "Import translations. DO NOT MERGE" into jb-mr1-aah-dev

* commit '11f72cd4c18dd5013b4425f2e2d5ac0294d3c304':
  Import translations. DO NOT MERGE
diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml
index c59e262..b77544b 100644
--- a/java/res/xml/rows_number_normal.xml
+++ b/java/res/xml/rows_number_normal.xml
@@ -33,6 +33,8 @@
             latin:keyStyle="numKeyStyle" />
         <Key
             latin:keyLabel="-"
+            latin:moreKeys="+"
+            latin:keyLabelFlags="hasPopupHint"
             latin:keyStyle="numFunctionalKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml/rows_phone.xml b/java/res/xml/rows_phone.xml
index 630b24e..9299c2a 100644
--- a/java/res/xml/rows_phone.xml
+++ b/java/res/xml/rows_phone.xml
@@ -34,6 +34,8 @@
             latin:keyStyle="num3KeyStyle" />
         <Key
             latin:keyLabel="-"
+            latin:moreKeys="+"
+            latin:keyLabelFlags="hasPopupHint"
             latin:keyStyle="numFunctionalKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml/rows_phone_symbols.xml b/java/res/xml/rows_phone_symbols.xml
index 7841c56..c13018e 100644
--- a/java/res/xml/rows_phone_symbols.xml
+++ b/java/res/xml/rows_phone_symbols.xml
@@ -37,6 +37,8 @@
             latin:keyStyle="numKeyStyle" />
         <Key
             latin:keyLabel="-"
+            latin:moreKeys="+"
+            latin:keyLabelFlags="hasPopupHint"
             latin:keyStyle="numFunctionalKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index 159f436..9a89eed 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -108,8 +108,8 @@
             CharSequence pickedWord, SuggestedWords suggestedWords, boolean dictionaryAvailable) {
         if (!dictionaryAvailable || TextUtils.isEmpty(pickedWord)
                 || CONSTRUCTOR_SuggestionSpan == null
-                || suggestedWords == null || suggestedWords.size() == 0
-                || suggestedWords.mIsPrediction || suggestedWords.mIsPunctuationSuggestions
+                || suggestedWords.isEmpty() || suggestedWords.mIsPrediction
+                || suggestedWords.mIsPunctuationSuggestions
                 || OBJ_SUGGESTIONS_MAX_SIZE == null) {
             return pickedWord;
         }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index fe6ad49..c0938cf 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1219,22 +1219,25 @@
         mConnection.performEditorAction(actionId);
     }
 
+    // TODO: Revise the language switch key behavior to make it much smarter and more reasonable.
     private void handleLanguageSwitchKey() {
-        final boolean includesOtherImes = mCurrentSettings.mIncludesOtherImesInLanguageSwitchList;
         final IBinder token = getWindow().getWindow().getAttributes().token;
+        if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) {
+            mImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
+            return;
+        }
         if (mShouldSwitchToLastSubtype) {
             final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype();
             final boolean lastSubtypeBelongsToThisIme =
                     ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(this, lastSubtype);
-            if ((includesOtherImes || lastSubtypeBelongsToThisIme)
-                    && mImm.switchToLastInputMethod(token)) {
+            if (lastSubtypeBelongsToThisIme  && mImm.switchToLastInputMethod(token)) {
                 mShouldSwitchToLastSubtype = false;
             } else {
-                mImm.switchToNextInputMethod(token, !includesOtherImes);
+                mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
                 mShouldSwitchToLastSubtype = true;
             }
         } else {
-            mImm.switchToNextInputMethod(token, !includesOtherImes);
+            mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
         }
     }
 
@@ -1515,8 +1518,8 @@
 
     private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
             final boolean dismissGestureFloatingPreviewText) {
-        final String batchInputText = (suggestedWords.size() > 0)
-                ? suggestedWords.getWord(0) : null;
+        final String batchInputText = suggestedWords.isEmpty()
+                ? null : suggestedWords.getWord(0);
         final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
         mainKeyboardView.showGestureFloatingPreviewText(batchInputText);
         showSuggestionStrip(suggestedWords, null);
@@ -1534,8 +1537,8 @@
     public void onEndBatchInput(final InputPointers batchPointers) {
         final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
                 batchPointers, this);
-        final String batchInputText = (suggestedWords.size() > 0)
-                ? suggestedWords.getWord(0) : null;
+        final String batchInputText = suggestedWords.isEmpty()
+                ? null : suggestedWords.getWord(0);
         if (TextUtils.isEmpty(batchInputText)) {
             return;
         }
@@ -1960,19 +1963,15 @@
 
     private void showSuggestionStrip(final SuggestedWords suggestedWords,
             final CharSequence typedWord) {
-        if (null == suggestedWords || suggestedWords.size() <= 0) {
+        if (suggestedWords.isEmpty()) {
             clearSuggestionStrip();
             return;
         }
         final CharSequence autoCorrection;
-        if (suggestedWords.size() > 0) {
-            if (suggestedWords.mWillAutoCorrect) {
-                autoCorrection = suggestedWords.getWord(1);
-            } else {
-                autoCorrection = typedWord;
-            }
+        if (suggestedWords.mWillAutoCorrect) {
+            autoCorrection = suggestedWords.getWord(1);
         } else {
-            autoCorrection = null;
+            autoCorrection = typedWord;
         }
         mWordComposer.setAutoCorrection(autoCorrection);
         final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 52e292a..ad94aff 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -53,6 +53,10 @@
         mIsPrediction = isPrediction;
     }
 
+    public boolean isEmpty() {
+        return mSuggestedWordInfoList.isEmpty();
+    }
+
     public int size() {
         return mSuggestedWordInfoList.size();
     }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 7b0231a..6210c92 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -17,13 +17,17 @@
 package com.android.inputmethod.latin.makedict;
 
 import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.CharEncoding;
 import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Stack;
 
@@ -90,7 +94,9 @@
 
             final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags,
                     formatOptions);
-            if (!isMovedGroup
+            final boolean isDeletedGroup = BinaryDictInputOutput.isDeletedGroup(info.mFlags,
+                    formatOptions);
+            if (!isMovedGroup && !isDeletedGroup
                     && info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word
                 words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
                 frequencies.put(info.mOriginalAddress, info.mFrequency);
@@ -165,19 +171,19 @@
             if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD;
 
             do {
-                int groupOffset = buffer.position() - header.mHeaderSize;
                 final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
-                groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount);
-
                 boolean foundNextCharGroup = false;
                 for (int i = 0; i < charGroupCount; ++i) {
                     final int charGroupPos = buffer.position();
                     final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
                             buffer.position(), header.mFormatOptions);
-                    if (BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
-                            header.mFormatOptions)) {
-                        continue;
-                    }
+                    final boolean isMovedGroup =
+                            BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
+                                    header.mFormatOptions);
+                    final boolean isDeletedGroup =
+                            BinaryDictInputOutput.isDeletedGroup(currentInfo.mFlags,
+                                    header.mFormatOptions);
+                    if (isMovedGroup) continue;
                     boolean same = true;
                     for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
                             p < currentInfo.mCharacters.length;
@@ -192,7 +198,8 @@
                     if (same) {
                         // found the group matches the word.
                         if (wordPos + currentInfo.mCharacters.length == wordLen) {
-                            if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL) {
+                            if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL
+                                    || isDeletedGroup) {
                                 return FormatSpec.NOT_VALID_WORD;
                             } else {
                                 return charGroupPos;
@@ -206,7 +213,6 @@
                         buffer.position(currentInfo.mChildrenAddress);
                         break;
                     }
-                    groupOffset = currentInfo.mEndAddress;
                 }
 
                 // If we found the next char group, it is under the file pointer.
@@ -228,6 +234,10 @@
         return FormatSpec.NOT_VALID_WORD;
     }
 
+    private static int markAsDeleted(final int flags) {
+        return (flags & (~FormatSpec.MASK_GROUP_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
+    }
+
     /**
      * Delete the word from the binary file.
      *
@@ -245,21 +255,58 @@
 
         buffer.position(wordPosition);
         final int flags = buffer.readUnsignedByte();
-        final int newFlags = flags ^ FormatSpec.FLAG_IS_TERMINAL;
         buffer.position(wordPosition);
-        buffer.put((byte)newFlags);
+        buffer.put((byte)markAsDeleted(flags));
     }
 
-    private static void putSInt24(final FusionDictionaryBufferInterface buffer,
+    /**
+     * @return the size written, in bytes. Always 3 bytes.
+     */
+    private static int writeSInt24ToBuffer(final FusionDictionaryBufferInterface buffer,
             final int value) {
         final int absValue = Math.abs(value);
         buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
         buffer.put((byte)((absValue >> 8) & 0xFF));
         buffer.put((byte)(absValue & 0xFF));
+        return 3;
     }
 
     /**
-     * Update a parent address in a CharGroup that is addressed by groupOriginAddress.
+     * @return the size written, in bytes. Always 3 bytes.
+     */
+    private static int writeSInt24ToStream(final OutputStream destination, final int value)
+            throws IOException {
+        final int absValue = Math.abs(value);
+        destination.write((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
+        destination.write((byte)((absValue >> 8) & 0xFF));
+        destination.write((byte)(absValue & 0xFF));
+        return 3;
+    }
+
+    /**
+     * @return the size written, in bytes. 1, 2, or 3 bytes.
+     */
+    private static int writeVariableAddress(final OutputStream destination, final int value)
+            throws IOException {
+        switch (BinaryDictInputOutput.getByteSize(value)) {
+        case 1:
+            destination.write((byte)value);
+            break;
+        case 2:
+            destination.write((byte)(0xFF & (value >> 8)));
+            destination.write((byte)(0xFF & value));
+            break;
+        case 3:
+            destination.write((byte)(0xFF & (value >> 16)));
+            destination.write((byte)(0xFF & (value >> 8)));
+            destination.write((byte)(0xFF & value));
+            break;
+        }
+        return BinaryDictInputOutput.getByteSize(value);
+    }
+
+    /**
+     * Update a parent address in a CharGroup that is referred to by groupOriginAddress.
      *
      * @param buffer the buffer to write.
      * @param groupOriginAddress the address of the group.
@@ -275,8 +322,227 @@
             throw new RuntimeException("this file format does not support parent addresses");
         }
         final int flags = buffer.readUnsignedByte();
+        if (BinaryDictInputOutput.isMovedGroup(flags, formatOptions)) {
+            // if the group is moved, the parent address is stored in the destination group.
+            // We are guaranteed to process the destination group later, so there is no need to
+            // update anything here.
+            buffer.position(originalPosition);
+            return;
+        }
+        if (DBG) {
+            MakedictLog.d("update parent address flags=" + flags + ", " + groupOriginAddress);
+        }
         final int parentOffset = newParentAddress - groupOriginAddress;
-        putSInt24(buffer, parentOffset);
+        writeSInt24ToBuffer(buffer, parentOffset);
         buffer.position(originalPosition);
     }
+
+    private static void skipCharGroup(final FusionDictionaryBufferInterface buffer,
+            final FormatOptions formatOptions) {
+        final int flags = buffer.readUnsignedByte();
+        BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
+        skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+        BinaryDictInputOutput.readChildrenAddress(buffer, flags, formatOptions);
+        if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
+        if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
+            final int shortcutsSize = buffer.readUnsignedShort();
+            buffer.position(buffer.position() + shortcutsSize
+                    - FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE);
+        }
+        if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) {
+            int bigramCount = 0;
+            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+                final int bigramFlags = buffer.readUnsignedByte();
+                switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
+                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
+                        buffer.readUnsignedByte();
+                        break;
+                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
+                        buffer.readUnsignedShort();
+                        break;
+                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
+                        buffer.readUnsignedInt24();
+                        break;
+                }
+                if ((bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT) == 0) break;
+            }
+            if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+                throw new RuntimeException("Too many bigrams in a group.");
+            }
+        }
+    }
+
+    /**
+     * Update parent addresses in a Node that is referred to by nodeOriginAddress.
+     *
+     * @param buffer the buffer to be modified.
+     * @param nodeOriginAddress the address of a modified Node.
+     * @param newParentAddress the address to be written
+     * @param formatOptions file format options
+     */
+    public static void updateParentAddresses(final FusionDictionaryBufferInterface buffer,
+            final int nodeOriginAddress, final int newParentAddress,
+            final FormatOptions formatOptions) {
+        final int originalPosition = buffer.position();
+        buffer.position(nodeOriginAddress);
+        do {
+            final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
+            for (int i = 0; i < count; ++i) {
+                updateParentAddress(buffer, buffer.position(), newParentAddress, formatOptions);
+                skipCharGroup(buffer, formatOptions);
+            }
+            final int forwardLinkAddress = buffer.readUnsignedInt24();
+            buffer.position(forwardLinkAddress);
+        } while (formatOptions.mSupportsDynamicUpdate
+                && buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
+        buffer.position(originalPosition);
+    }
+
+    private static void skipString(final FusionDictionaryBufferInterface buffer,
+            final boolean hasMultipleChars) {
+        if (hasMultipleChars) {
+            int character = CharEncoding.readChar(buffer);
+            while (character != FormatSpec.INVALID_CHARACTER) {
+                character = CharEncoding.readChar(buffer);
+            }
+        } else {
+            CharEncoding.readChar(buffer);
+        }
+    }
+
+    /**
+     * Write a string to a stream.
+     *
+     * @param destination the stream to write.
+     * @param word the string to be written.
+     * @return the size written, in bytes.
+     * @throws IOException
+     */
+    private static int writeString(final OutputStream destination, final String word)
+            throws IOException {
+        int size = 0;
+        final int length = word.length();
+        for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+            final int codePoint = word.codePointAt(i);
+            if (CharEncoding.getCharSize(codePoint) == 1) {
+                destination.write((byte)codePoint);
+                size++;
+            } else {
+                destination.write((byte)(0xFF & (codePoint >> 16)));
+                destination.write((byte)(0xFF & (codePoint >> 8)));
+                destination.write((byte)(0xFF & codePoint));
+                size += 3;
+            }
+        }
+        destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+        size++;
+        return size;
+    }
+
+    /**
+     * Update a children address in a CharGroup that is addressed by groupOriginAddress.
+     *
+     * @param buffer the buffer to write.
+     * @param groupOriginAddress the address of the group.
+     * @param newChildrenAddress the absolute address of the child.
+     * @param formatOptions file format options.
+     */
+    public static void updateChildrenAddress(final FusionDictionaryBufferInterface buffer,
+            final int groupOriginAddress, final int newChildrenAddress,
+            final FormatOptions formatOptions) {
+        final int originalPosition = buffer.position();
+        buffer.position(groupOriginAddress);
+        final int flags = buffer.readUnsignedByte();
+        final int parentAddress = BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
+        skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+        if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
+        final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS
+                ? FormatSpec.NO_CHILDREN_ADDRESS : newChildrenAddress - buffer.position();
+        writeSInt24ToBuffer(buffer, childrenOffset);
+        buffer.position(originalPosition);
+    }
+
+    /**
+     * Write a char group to an output stream.
+     * A char group is an in-memory representation of a node in trie.
+     * A char group info is an on-disk representation of a node.
+     *
+     * @param destination the stream to write.
+     * @param info the char group info to be written.
+     * @return the size written, in bytes.
+     */
+    public static int writeCharGroup(final OutputStream destination, final CharGroupInfo info)
+            throws IOException {
+        int size = 1;
+        destination.write((byte)info.mFlags);
+        final int parentOffset = info.mParentAddress == FormatSpec.NO_PARENT_ADDRESS ?
+                FormatSpec.NO_PARENT_ADDRESS : info.mParentAddress - info.mOriginalAddress;
+        size += writeSInt24ToStream(destination, parentOffset);
+
+        for (int i = 0; i < info.mCharacters.length; ++i) {
+            if (CharEncoding.getCharSize(info.mCharacters[i]) == 1) {
+                destination.write((byte)info.mCharacters[i]);
+                size++;
+            } else {
+                size += writeSInt24ToStream(destination, info.mCharacters[i]);
+            }
+        }
+        if (info.mCharacters.length > 1) {
+            destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+            size++;
+        }
+
+        if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
+            destination.write((byte)info.mFrequency);
+            size++;
+        }
+
+        final int childrenOffset = info.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS ?
+                0 : info.mChildrenAddress - info.mOriginalAddress;
+        writeSInt24ToStream(destination, childrenOffset);
+
+        if (info.mShortcutTargets != null && info.mShortcutTargets.size() > 0) {
+            final int shortcutListSize =
+                    BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets);
+            destination.write((byte)(shortcutListSize >> 8));
+            destination.write((byte)(shortcutListSize & 0xFF));
+            size += 2;
+            final Iterator<WeightedString> shortcutIterator = info.mShortcutTargets.iterator();
+            while (shortcutIterator.hasNext()) {
+                final WeightedString target = shortcutIterator.next();
+                destination.write((byte)BinaryDictInputOutput.makeShortcutFlags(
+                        shortcutIterator.hasNext(), target.mFrequency));
+                size++;
+                size += writeString(destination, target.mWord);
+            }
+        }
+
+        if (info.mBigrams != null) {
+            // TODO: Consolidate this code with the code that computes the size of the bigram list
+            //        in BinaryDictionaryInputOutput#computeActualNodeSize
+            for (int i = 0; i < info.mBigrams.size(); ++i) {
+                final int bigramOffset = info.mBigrams.get(i).mAddress - info.mOriginalAddress;
+                final int bigramFrequency = info.mBigrams.get(i).mFrequency;
+                int bigramFlags = (i < info.mBigrams.size() - 1)
+                        ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0;
+                bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0;
+                switch (BinaryDictInputOutput.getByteSize(bigramOffset)) {
+                case 1:
+                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+                    break;
+                case 2:
+                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+                    break;
+                case 3:
+                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+                    break;
+                }
+                bigramFlags |= bigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
+                destination.write((byte)bigramFlags);
+                size++;
+                size += writeVariableAddress(destination, Math.abs(bigramOffset));
+            }
+        }
+        return size;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index b431a4d..78171d0 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -124,8 +124,7 @@
     /**
      * A class grouping utility function for our specific character encoding.
      */
-    private static final class CharEncoding {
-
+    static final class CharEncoding {
         private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
         private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
 
@@ -154,7 +153,7 @@
          * @param character the character code.
          * @return the size in binary encoded-form, either 1 or 3 bytes.
          */
-        private static int getCharSize(final int character) {
+        static int getCharSize(final int character) {
             // See char encoding in FusionDictionary.java
             if (fitsOnOneByte(character)) return 1;
             if (FormatSpec.INVALID_CHARACTER == character) return 1;
@@ -263,7 +262,7 @@
          * @param buffer the buffer, positioned over an encoded character.
          * @return the character code.
          */
-        private static int readChar(final FusionDictionaryBufferInterface buffer) {
+        static int readChar(final FusionDictionaryBufferInterface buffer) {
             int character = buffer.readUnsignedByte();
             if (!fitsOnOneByte(character)) {
                 if (FormatSpec.GROUP_CHARACTERS_TERMINATOR == character) {
@@ -338,7 +337,7 @@
      * This is known in advance and does not change according to position in the file
      * like address lists do.
      */
-    private static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
+    static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
         if (null == shortcutList) return 0;
         int size = FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
         for (final WeightedString shortcut : shortcutList) {
@@ -403,6 +402,14 @@
     }
 
     /**
+     * Helper method to check whether the group is deleted.
+     */
+    public static boolean isDeletedGroup(final int flags, final FormatOptions formatOptions) {
+        return formatOptions.mSupportsDynamicUpdate
+                && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
+    }
+
+    /**
      * Helper method to check whether the dictionary can be updated dynamically.
      */
     public static boolean supportsDynamicUpdate(final FormatOptions options) {
@@ -439,7 +446,7 @@
      * @param address the address
      * @return the byte size.
      */
-    private static int getByteSize(final int address) {
+    static int getByteSize(final int address) {
         assert(address <= UINT24_MAX);
         if (!hasChildrenAddress(address)) {
             return 0;
@@ -721,51 +728,58 @@
         return 3;
     }
 
+    /**
+     * Makes the flag value for a char group.
+     *
+     * @param hasMultipleChars whether the group has multiple chars.
+     * @param isTerminal whether the group is terminal.
+     * @param childrenAddressSize the size of a children address.
+     * @param hasShortcuts whether the group has shortcuts.
+     * @param hasBigrams whether the group has bigrams.
+     * @param isNotAWord whether the group is not a word.
+     * @param isBlackListEntry whether the group is a blacklist entry.
+     * @param formatOptions file format options.
+     * @return the flags
+     */
+    static int makeCharGroupFlags(final boolean hasMultipleChars, final boolean isTerminal,
+            final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
+            final boolean isNotAWord, final boolean isBlackListEntry,
+            final FormatOptions formatOptions) {
+        byte flags = 0;
+        if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
+        if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
+        if (formatOptions.mSupportsDynamicUpdate) {
+            flags |= FormatSpec.FLAG_IS_NOT_MOVED;
+        } else if (true) {
+            switch (childrenAddressSize) {
+                case 1:
+                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
+                    break;
+                case 2:
+                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
+                    break;
+                case 3:
+                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
+                    break;
+                case 0:
+                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS;
+                    break;
+                default:
+                    throw new RuntimeException("Node with a strange address");
+            }
+        }
+        if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
+        if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS;
+        if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
+        if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED;
+        return flags;
+    }
+
     private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
             final int childrenOffset, final FormatOptions formatOptions) {
-        byte flags = 0;
-        if (group.mChars.length > 1) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
-        if (group.mFrequency >= 0) {
-            flags |= FormatSpec.FLAG_IS_TERMINAL;
-        }
-        if (null != group.mChildren) {
-            final int byteSize = formatOptions.mSupportsDynamicUpdate
-                    ? FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE : getByteSize(childrenOffset);
-            switch (byteSize) {
-            case 1:
-                flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
-                break;
-            case 2:
-                flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
-                break;
-            case 3:
-                flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
-                break;
-            default:
-                throw new RuntimeException("Node with a strange address");
-            }
-        } else if (formatOptions.mSupportsDynamicUpdate) {
-            flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
-        }
-        if (null != group.mShortcutTargets) {
-            if (DBG && 0 == group.mShortcutTargets.size()) {
-                throw new RuntimeException("0-sized shortcut list must be null");
-            }
-            flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
-        }
-        if (null != group.mBigrams) {
-            if (DBG && 0 == group.mBigrams.size()) {
-                throw new RuntimeException("0-sized bigram list must be null");
-            }
-            flags |= FormatSpec.FLAG_HAS_BIGRAMS;
-        }
-        if (group.mIsNotAWord) {
-            flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
-        }
-        if (group.mIsBlacklistEntry) {
-            flags |= FormatSpec.FLAG_IS_BLACKLISTED;
-        }
-        return flags;
+        return (byte) makeCharGroupFlags(group.mChars.length > 1, group.mFrequency >= 0,
+                getByteSize(childrenOffset), group.mShortcutTargets != null, group.mBigrams != null,
+                group.mIsNotAWord, group.mIsBlacklistEntry, formatOptions);
     }
 
     /**
@@ -859,7 +873,7 @@
      * @param frequency the frequency of the attribute, 0..15
      * @return the flags
      */
-    private static final int makeShortcutFlags(final boolean more, final int frequency) {
+    static final int makeShortcutFlags(final boolean more, final int frequency) {
         return (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
                 + (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY);
     }
@@ -897,6 +911,7 @@
      */
     private static int writePlacedNode(final FusionDictionary dict, byte[] buffer,
             final Node node, final FormatOptions formatOptions) {
+        // TODO: Make the code in common with BinaryDictIOUtils#writeCharGroup
         int index = node.mCachedAddress;
 
         final int groupCount = node.mData.size();
@@ -1194,7 +1209,7 @@
         }
     }
 
-    private static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
+    static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
             final int optionFlags, final FormatOptions options) {
         if (options.mSupportsDynamicUpdate) {
             final int address = buffer.readUnsignedInt24();
@@ -1219,7 +1234,7 @@
         }
     }
 
-    private static int readParentAddress(final FusionDictionaryBufferInterface buffer,
+    static int readParentAddress(final FusionDictionaryBufferInterface buffer,
             final FormatOptions formatOptions) {
         if (supportsDynamicUpdate(formatOptions)) {
             final int parentAddress = buffer.readUnsignedInt24();
@@ -1290,7 +1305,8 @@
         ArrayList<PendingAttribute> bigrams = null;
         if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
             bigrams = new ArrayList<PendingAttribute>();
-            while (true) {
+            int bigramCount = 0;
+            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
                 final int bigramFlags = buffer.readUnsignedByte();
                 ++addressPointer;
                 final int sign = 0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE)
@@ -1318,6 +1334,9 @@
                         bigramAddress));
                 if (0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
             }
+            if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+                MakedictLog.d("too many bigrams in a group.");
+            }
         }
         return new CharGroupInfo(originalGroupAddress, addressPointer, flags, characters, frequency,
                 parentAddress, childrenAddress, shortcutTargets, bigrams);
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index b3fbb9f..ca851c6 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -59,9 +59,11 @@
      * l |                                   10 = 2 bytes     : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
      * a |                                   11 = 3 bytes     : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
      * g | ELSE
-     * s |   is moved ?              2 bits, 11 = no
-     *   |                                   01 = yes
+     * s |   is moved ?              2 bits, 11 = no          : FLAG_IS_NOT_MOVED
+     *   |                              This must be the same as FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
+     *   |                                   01 = yes         : FLAG_IS_MOVED
      *   |                        the new address is stored in the same place as the parent address
+     *   |   is deleted?                     10 = yes         : FLAG_IS_DELETED
      *   | has several chars ?         1 bit, 1 = yes, 0 = no   : FLAG_HAS_MULTIPLE_CHARS
      *   | has a terminal ?            1 bit, 1 = yes, 0 = no   : FLAG_IS_TERMINAL
      *   | has shortcut targets ?      1 bit, 1 = yes, 0 = no   : FLAG_HAS_SHORTCUT_TARGETS
@@ -170,6 +172,7 @@
     static final int PARENT_ADDRESS_SIZE = 3;
     static final int FORWARD_LINK_ADDRESS_SIZE = 3;
 
+    // These flags are used only in the static dictionary.
     static final int MASK_GROUP_ADDRESS_TYPE = 0xC0;
     static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
     static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
@@ -183,7 +186,12 @@
     static final int FLAG_HAS_BIGRAMS = 0x04;
     static final int FLAG_IS_NOT_A_WORD = 0x02;
     static final int FLAG_IS_BLACKLISTED = 0x01;
-    static final int FLAG_IS_MOVED = 0x40;
+
+    // These flags are used only in the dynamic dictionary.
+    static final int FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE = 0x40;
+    static final int FLAG_IS_MOVED = 0x00 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
+    static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
+    static final int FLAG_IS_DELETED = 0x80;
 
     static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
     static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
@@ -210,6 +218,7 @@
 
     static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
     static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767
+    static final int MAX_BIGRAMS_IN_A_GROUP = 10000;
 
     static final int MAX_TERMINAL_FREQUENCY = 255;
     static final int MAX_BIGRAM_FREQUENCY = 15;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index e9bf0fa..4ad82ab 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -22,7 +22,6 @@
 
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -175,11 +174,11 @@
         }
 
         public Builder layout(final SuggestedWords suggestions, final int fromPos,
-                final int maxWidth, final int minWidth, final int maxRow) {
-            final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
+                final int maxWidth, final int minWidth, final int maxRow,
+                final Keyboard parentKeyboard) {
             final int xmlId = R.xml.kbd_suggestions_pane_template;
-            load(xmlId, keyboard.mId);
-            mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2;
+            load(xmlId, parentKeyboard.mId);
+            mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
 
             final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow,
                     mPaneView);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 9b9a354..740476f 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -56,17 +56,17 @@
     final KeyboardActionListener mSuggestionsPaneListener =
             new KeyboardActionListener.Adapter() {
         @Override
-        public void onPressKey(int primaryCode) {
+        public void onPressKey(final int primaryCode) {
             mListener.onPressKey(primaryCode);
         }
 
         @Override
-        public void onReleaseKey(int primaryCode, boolean withSliding) {
+        public void onReleaseKey(final int primaryCode, final boolean withSliding) {
             mListener.onReleaseKey(primaryCode, withSliding);
         }
 
         @Override
-        public void onCodeInput(int primaryCode, int x, int y) {
+        public void onCodeInput(final int primaryCode, final int x, final int y) {
             final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE;
             if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
                 mListener.onCustomRequest(index);
@@ -79,11 +79,12 @@
         }
     };
 
-    public MoreSuggestionsView(Context context, AttributeSet attrs) {
+    public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.moreSuggestionsViewStyle);
     }
 
-    public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+    public MoreSuggestionsView(final Context context, final AttributeSet attrs,
+            final int defStyle) {
         super(context, attrs, defStyle);
 
         final Resources res = context.getResources();
@@ -94,7 +95,7 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
         final Keyboard keyboard = getKeyboard();
         if (keyboard != null) {
             final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
@@ -106,7 +107,7 @@
     }
 
     @Override
-    public void setKeyboard(Keyboard keyboard) {
+    public void setKeyboard(final Keyboard keyboard) {
         super.setKeyboard(keyboard);
         mModalPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop());
         mSlidingPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
@@ -134,15 +135,16 @@
     }
 
     @Override
-    public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
         // Suggestions pane needs no pop-up key preview displayed, so we pass always false with a
         // delay of 0. The delay does not matter actually since the popup is not shown anyway.
         super.setKeyPreviewPopupEnabled(false, 0);
     }
 
     @Override
-    public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
-            PopupWindow window, KeyboardActionListener listener) {
+    public void showMoreKeysPanel(final View parentView, final Controller controller,
+            final int pointX, final int pointY, final PopupWindow window,
+            final KeyboardActionListener listener) {
         mController = controller;
         mListener = listener;
         final View container = (View)getParent();
@@ -175,12 +177,12 @@
     }
 
     @Override
-    public int translateX(int x) {
+    public int translateX(final int x) {
         return x - mOriginX;
     }
 
     @Override
-    public int translateY(int y) {
+    public int translateY(final int y) {
         return y - mOriginY;
     }
 
@@ -207,7 +209,7 @@
     };
 
     @Override
-    public boolean onTouchEvent(MotionEvent me) {
+    public boolean onTouchEvent(final MotionEvent me) {
         final int action = me.getAction();
         final long eventTime = me.getEventTime();
         final int index = me.getActionIndex();
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e926fa2..6056af9 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -52,7 +52,9 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.MoreKeysPanel;
 import com.android.inputmethod.keyboard.PointerTracker;
@@ -83,7 +85,7 @@
     static final boolean DBG = LatinImeLogger.sDBG;
 
     private final ViewGroup mSuggestionsStrip;
-    private KeyboardView mKeyboardView;
+    KeyboardView mKeyboardView;
 
     private final View mMoreSuggestionsContainer;
     private final MoreSuggestionsView mMoreSuggestionsView;
@@ -97,8 +99,8 @@
     private final PopupWindow mPreviewPopup;
     private final TextView mPreviewText;
 
-    private Listener mListener;
-    private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+    Listener mListener;
+    SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
 
     private final SuggestionStripViewParams mParams;
     private static final float MIN_TEXT_XSCALE = 0.70f;
@@ -108,12 +110,12 @@
     private static final class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
         private static final int MSG_HIDE_PREVIEW = 0;
 
-        public UiHandler(SuggestionStripView outerInstance) {
+        public UiHandler(final SuggestionStripView outerInstance) {
             super(outerInstance);
         }
 
         @Override
-        public void dispatchMessage(Message msg) {
+        public void dispatchMessage(final Message msg) {
             final SuggestionStripView suggestionStripView = getOuterInstance();
             switch (msg.what) {
             case MSG_HIDE_PREVIEW:
@@ -177,8 +179,9 @@
         private final TextView mLeftwardsArrowView;
         private final TextView mHintToSaveView;
 
-        public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle,
-                ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) {
+        public SuggestionStripViewParams(final Context context, final AttributeSet attrs,
+                final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers,
+                final ArrayList<TextView> infos) {
             mWords = words;
             mDividers = dividers;
             mInfos = infos;
@@ -250,7 +253,7 @@
             return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
         }
 
-        public int setMoreSuggestionsHeight(int remainingHeight) {
+        public int setMoreSuggestionsHeight(final int remainingHeight) {
             final int currentHeight = getMoreSuggestionsHeight();
             if (currentHeight <= remainingHeight) {
                 return currentHeight;
@@ -262,7 +265,8 @@
             return newHeight;
         }
 
-        private static Drawable getMoreSuggestionsHint(Resources res, float textSize, int color) {
+        private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
+                final int color) {
             final Paint paint = new Paint();
             paint.setAntiAlias(true);
             paint.setTextAlign(Align.CENTER);
@@ -279,7 +283,8 @@
             return new BitmapDrawable(res, buffer);
         }
 
-        private CharSequence getStyledSuggestionWord(SuggestedWords suggestedWords, int pos) {
+        private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
+                final int pos) {
             final CharSequence word = suggestedWords.getWord(pos);
             final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
             final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid;
@@ -299,7 +304,7 @@
             return spannedWord;
         }
 
-        private int getWordPosition(int index, SuggestedWords suggestedWords) {
+        private int getWordPosition(final int index, final SuggestedWords suggestedWords) {
             // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
             // suggestions.
             final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0;
@@ -312,7 +317,8 @@
             }
         }
 
-        private int getSuggestionTextColor(int index, SuggestedWords suggestedWords, int pos) {
+        private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords,
+                final int pos) {
             // TODO: Need to revisit this logic with bigram suggestions
             final boolean isSuggested = (pos != 0);
 
@@ -355,8 +361,8 @@
             params.gravity = Gravity.CENTER;
         }
 
-        public void layout(SuggestedWords suggestedWords, ViewGroup stripView, ViewGroup placer,
-                int stripWidth) {
+        public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView,
+                final ViewGroup placer, final int stripWidth) {
             if (suggestedWords.mIsPunctuationSuggestions) {
                 layoutPunctuationSuggestions(suggestedWords, stripView);
                 return;
@@ -418,14 +424,14 @@
             }
         }
 
-        private int getSuggestionWidth(int index, int maxWidth) {
+        private int getSuggestionWidth(final int index, final int maxWidth) {
             final int paddings = mPadding * mSuggestionsCountInStrip;
             final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
             final int availableWidth = maxWidth - paddings - dividers;
             return (int)(availableWidth * getSuggestionWeight(index));
         }
 
-        private float getSuggestionWeight(int index) {
+        private float getSuggestionWeight(final int index) {
             if (index == mCenterSuggestionIndex) {
                 return mCenterSuggestionWeight;
             } else {
@@ -434,7 +440,7 @@
             }
         }
 
-        private void setupTexts(SuggestedWords suggestedWords, int countInStrip) {
+        private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) {
             mTexts.clear();
             final int count = Math.min(suggestedWords.size(), countInStrip);
             for (int pos = 0; pos < count; pos++) {
@@ -447,8 +453,8 @@
             }
         }
 
-        private void layoutPunctuationSuggestions(SuggestedWords suggestedWords,
-                ViewGroup stripView) {
+        private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
+                final ViewGroup stripView) {
             final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
             for (int index = 0; index < countInStrip; index++) {
                 if (index != 0) {
@@ -469,8 +475,8 @@
             mMoreSuggestionsAvailable = false;
         }
 
-        public void layoutAddToDictionaryHint(CharSequence word, ViewGroup stripView,
-                int stripWidth, CharSequence hintText, OnClickListener listener) {
+        public void layoutAddToDictionaryHint(final CharSequence word, final ViewGroup stripView,
+                final int stripWidth, final CharSequence hintText, final OnClickListener listener) {
             final int width = stripWidth - mDividerWidth - mPadding * 2;
 
             final TextView wordView = mWordToSaveView;
@@ -511,11 +517,11 @@
             return (CharSequence)mWordToSaveView.getTag();
         }
 
-        public boolean isAddToDictionaryShowing(View v) {
+        public boolean isAddToDictionaryShowing(final View v) {
             return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView;
         }
 
-        private static void setLayoutWeight(View v, float weight, int height) {
+        private static void setLayoutWeight(final View v, final float weight, final int height) {
             final ViewGroup.LayoutParams lp = v.getLayoutParams();
             if (lp instanceof LinearLayout.LayoutParams) {
                 final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
@@ -525,7 +531,8 @@
             }
         }
 
-        private static float getTextScaleX(CharSequence text, int maxWidth, TextPaint paint) {
+        private static float getTextScaleX(final CharSequence text, final int maxWidth,
+                final TextPaint paint) {
             paint.setTextScaleX(1.0f);
             final int width = getTextWidth(text, paint);
             if (width <= maxWidth) {
@@ -534,8 +541,8 @@
             return maxWidth / (float)width;
         }
 
-        private static CharSequence getEllipsizedText(CharSequence text, int maxWidth,
-                TextPaint paint) {
+        private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
+                final TextPaint paint) {
             if (text == null) return null;
             paint.setTextScaleX(1.0f);
             final int width = getTextWidth(text, paint);
@@ -556,7 +563,7 @@
             return ellipsized;
         }
 
-        private static int getTextWidth(CharSequence text, TextPaint paint) {
+        private static int getTextWidth(final CharSequence text, final TextPaint paint) {
             if (TextUtils.isEmpty(text)) return 0;
             final Typeface savedTypeface = paint.getTypeface();
             paint.setTypeface(getTextTypeface(text));
@@ -571,7 +578,7 @@
             return width;
         }
 
-        private static Typeface getTextTypeface(CharSequence text) {
+        private static Typeface getTextTypeface(final CharSequence text) {
             if (!(text instanceof SpannableString))
                 return Typeface.DEFAULT;
 
@@ -593,11 +600,12 @@
      * @param context
      * @param attrs
      */
-    public SuggestionStripView(Context context, AttributeSet attrs) {
+    public SuggestionStripView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.suggestionStripViewStyle);
     }
 
-    public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) {
+    public SuggestionStripView(final Context context, final AttributeSet attrs,
+            final int defStyle) {
         super(context, attrs, defStyle);
 
         final LayoutInflater inflater = LayoutInflater.from(context);
@@ -658,15 +666,12 @@
      * A connection back to the input method.
      * @param listener
      */
-    public void setListener(Listener listener, View inputView) {
+    public void setListener(final Listener listener, final View inputView) {
         mListener = listener;
         mKeyboardView = (KeyboardView)inputView.findViewById(R.id.keyboard_view);
     }
 
-    public void setSuggestions(SuggestedWords suggestedWords) {
-        if (suggestedWords == null)
-            return;
-
+    public void setSuggestions(final SuggestedWords suggestedWords) {
         clear();
         mSuggestedWords = suggestedWords;
         mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
@@ -675,7 +680,7 @@
         }
     }
 
-    public int setMoreSuggestionsHeight(int remainingHeight) {
+    public int setMoreSuggestionsHeight(final int remainingHeight) {
         return mParams.setMoreSuggestionsHeight(remainingHeight);
     }
 
@@ -684,7 +689,7 @@
                 && mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0));
     }
 
-    public void showAddToDictionaryHint(CharSequence word, CharSequence hintText) {
+    public void showAddToDictionaryHint(final CharSequence word, final CharSequence hintText) {
         clear();
         mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this);
     }
@@ -708,14 +713,14 @@
         dismissMoreSuggestions();
     }
 
-    private void hidePreview() {
+    void hidePreview() {
         mPreviewPopup.dismiss();
     }
 
     private final KeyboardActionListener mMoreSuggestionsListener =
             new KeyboardActionListener.Adapter() {
         @Override
-        public boolean onCustomRequest(int requestCode) {
+        public boolean onCustomRequest(final int requestCode) {
             final int index = requestCode;
             final CharSequence word = mSuggestedWords.getWord(index);
             mListener.pickSuggestionManually(index, word);
@@ -737,7 +742,7 @@
         }
     };
 
-    private boolean dismissMoreSuggestions() {
+    boolean dismissMoreSuggestions() {
         if (mMoreSuggestionsWindow.isShowing()) {
             mMoreSuggestionsWindow.dismiss();
             return true;
@@ -746,41 +751,42 @@
     }
 
     @Override
-    public boolean onLongClick(View view) {
+    public boolean onLongClick(final View view) {
         return showMoreSuggestions();
     }
 
-    private boolean showMoreSuggestions() {
-        final SuggestionStripViewParams params = mParams;
-        if (params.mMoreSuggestionsAvailable) {
-            final int stripWidth = getWidth();
-            final View container = mMoreSuggestionsContainer;
-            final int maxWidth = stripWidth - container.getPaddingLeft()
-                    - container.getPaddingRight();
-            final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
-            builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
-                    (int)(maxWidth * params.mMinMoreSuggestionsWidth),
-                    params.getMaxMoreSuggestionsRow());
-            mMoreSuggestionsView.setKeyboard(builder.build());
-            container.measure(
-                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-            final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
-            final int pointX = stripWidth / 2;
-            final int pointY = -params.mMoreSuggestionsBottomGap;
-            moreKeysPanel.showMoreKeysPanel(
-                    this, mMoreSuggestionsController, pointX, pointY,
-                    mMoreSuggestionsWindow, mMoreSuggestionsListener);
-            mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
-            mOriginX = mLastX;
-            mOriginY = mLastY;
-            mKeyboardView.dimEntireKeyboard(true);
-            for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
-                mWords.get(i).setPressed(false);
-            }
-            return true;
+    boolean showMoreSuggestions() {
+        final Keyboard parentKeyboard = KeyboardSwitcher.getInstance().getKeyboard();
+        if (parentKeyboard == null) {
+            return false;
         }
-        return false;
+        final SuggestionStripViewParams params = mParams;
+        if (!params.mMoreSuggestionsAvailable) {
+            return false;
+        }
+        final int stripWidth = getWidth();
+        final View container = mMoreSuggestionsContainer;
+        final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
+        final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
+        builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
+                (int)(maxWidth * params.mMinMoreSuggestionsWidth),
+                params.getMaxMoreSuggestionsRow(), parentKeyboard);
+        mMoreSuggestionsView.setKeyboard(builder.build());
+        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+        final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
+        final int pointX = stripWidth / 2;
+        final int pointY = -params.mMoreSuggestionsBottomGap;
+        moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
+                mMoreSuggestionsWindow, mMoreSuggestionsListener);
+        mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
+        mOriginX = mLastX;
+        mOriginY = mLastY;
+        mKeyboardView.dimEntireKeyboard(true);
+        for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
+            mWords.get(i).setPressed(false);
+        }
+        return true;
     }
 
     // Working variables for onLongClick and dispatchTouchEvent.
@@ -807,7 +813,7 @@
     };
 
     @Override
-    public boolean dispatchTouchEvent(MotionEvent me) {
+    public boolean dispatchTouchEvent(final MotionEvent me) {
         if (!mMoreSuggestionsWindow.isShowing()
                 || mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
             mLastX = (int)me.getX();
@@ -849,7 +855,7 @@
     }
 
     @Override
-    public void onClick(View view) {
+    public void onClick(final View view) {
         if (mParams.isAddToDictionaryShowing(view)) {
             mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
             clear();