Merge "Add resolveBigramPositions."
diff --git a/java/res/values-sw600dp-land/config.xml b/java/res/values-sw540dp-land/config.xml
similarity index 100%
rename from java/res/values-sw600dp-land/config.xml
rename to java/res/values-sw540dp-land/config.xml
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw540dp-land/dimens.xml
similarity index 100%
rename from java/res/values-sw600dp-land/dimens.xml
rename to java/res/values-sw540dp-land/dimens.xml
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw540dp/config.xml
similarity index 100%
rename from java/res/values-sw600dp/config.xml
rename to java/res/values-sw540dp/config.xml
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw540dp/dimens.xml
similarity index 100%
rename from java/res/values-sw600dp/dimens.xml
rename to java/res/values-sw540dp/dimens.xml
diff --git a/java/res/values-sw600dp/touch-position-correction.xml b/java/res/values-sw540dp/touch-position-correction.xml
similarity index 100%
rename from java/res/values-sw600dp/touch-position-correction.xml
rename to java/res/values-sw540dp/touch-position-correction.xml
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
new file mode 100644
index 0000000..b119d6c
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.os.Build;
+import android.view.inputmethod.InputMethodSubtype;
+
+import java.lang.reflect.Constructor;
+
+public final class InputMethodSubtypeCompatUtils {
+    private static final String TAG = InputMethodSubtypeCompatUtils.class.getSimpleName();
+    // Note that InputMethodSubtype(int nameId, int iconId, String locale, String mode,
+    // String extraValue, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id)
+    // has been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1).
+    private static final Constructor<?> CONSTRUCTOR_INPUT_METHOD_SUBTYPE =
+            CompatUtils.getConstructor(InputMethodSubtype.class,
+                    Integer.TYPE, Integer.TYPE, String.class, String.class, String.class,
+                    Boolean.TYPE, Boolean.TYPE, Integer.TYPE);
+    static {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            if (CONSTRUCTOR_INPUT_METHOD_SUBTYPE == null) {
+                android.util.Log.w(TAG, "Warning!!! Constructor is not defined.");
+            }
+        }
+    }
+    private InputMethodSubtypeCompatUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    public static InputMethodSubtype newInputMethodSubtype(int nameId, int iconId, String locale,
+            String mode, String extraValue, boolean isAuxiliary,
+            boolean overridesImplicitlyEnabledSubtype, int id) {
+        if (CONSTRUCTOR_INPUT_METHOD_SUBTYPE == null
+                || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return new InputMethodSubtype(nameId, iconId, locale, mode, extraValue, isAuxiliary,
+                    overridesImplicitlyEnabledSubtype);
+        }
+        return (InputMethodSubtype) CompatUtils.newInstance(CONSTRUCTOR_INPUT_METHOD_SUBTYPE,
+                nameId, iconId, locale, mode, extraValue, isAuxiliary,
+                overridesImplicitlyEnabledSubtype, id);
+    }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
index c28d729..4366348 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.dictionarypack;
 
+import com.android.inputmethod.latin.utils.FragmentUtils;
+
 import android.content.Intent;
 import android.os.Bundle;
 import android.preference.PreferenceActivity;
@@ -45,6 +47,6 @@
     // TODO: Uncomment the override annotation once we start using SDK version 19.
     // @Override
     public boolean isValidFragment(String fragmentName) {
-        return fragmentName.equals(DEFAULT_FRAGMENT);
+        return FragmentUtils.isValidFragment(fragmentName);
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 9779c68..f123735 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -162,9 +162,11 @@
                 addShownCategoryId(CATEGORY_ID_OBJECTS);
                 addShownCategoryId(CATEGORY_ID_NATURE);
                 addShownCategoryId(CATEGORY_ID_PLACES);
-                mCurrentCategoryId = CATEGORY_ID_PEOPLE;
+                mCurrentCategoryId =
+                        Settings.readLastShownEmojiCategoryId(mPrefs, CATEGORY_ID_PEOPLE);
             } else {
-                mCurrentCategoryId = CATEGORY_ID_SYMBOLS;
+                mCurrentCategoryId =
+                        Settings.readLastShownEmojiCategoryId(mPrefs, CATEGORY_ID_SYMBOLS);
             }
             addShownCategoryId(CATEGORY_ID_SYMBOLS);
             addShownCategoryId(CATEGORY_ID_EMOTICONS);
@@ -222,6 +224,7 @@
 
         public void setCurrentCategoryId(int categoryId) {
             mCurrentCategoryId = categoryId;
+            Settings.writeLastShownEmojiCategoryId(mPrefs, categoryId);
         }
 
         public void setCurrentCategoryPageId(int id) {
@@ -233,7 +236,7 @@
         }
 
         public void saveLastTypedCategoryPage() {
-            Settings.writeEmojiCategoryLastTypedId(
+            Settings.writeLastTypedEmojiCategoryPageId(
                     mPrefs, mCurrentCategoryId, mCurrentCategoryPageId);
         }
 
@@ -254,7 +257,7 @@
         // Returns the view pager's page position for the categoryId
         public int getPageIdFromCategoryId(int categoryId) {
             final int lastSavedCategoryPageId =
-                    Settings.readEmojiCategoryLastTypedId(mPrefs, categoryId);
+                    Settings.readLastTypedEmojiCategoryPageId(mPrefs, categoryId);
             int sum = 0;
             for (int i = 0; i < mShownCategories.size(); ++i) {
                 final CategoryProperties props = mShownCategories.get(i);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index 09766ac..3133e54 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -184,12 +184,12 @@
 
     private int getKeyY0(final int index) {
         final int row = index / mColumnsNum;
-        return row * mVerticalStep;
+        return row * mVerticalStep + mVerticalGap / 2;
     }
 
     private int getKeyY1(final int index) {
         final int row = index / mColumnsNum + 1;
-        return row * mVerticalStep;
+        return row * mVerticalStep + mVerticalGap / 2;
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index bcb80b4..e769e3c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -278,7 +278,7 @@
         /* 50 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
         /* 51 */ "$",
         /* 52 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1",
-        /* 53 */ "!fixedColumnOrder!3,!,\\,,?,:,;,@",
+        /* 53 */ "!fixedColumnOrder!4,#,!,\\,,?,-,:,',@",
         // U+2020: "†" DAGGER
         // U+2021: "‡" DOUBLE DAGGER
         // U+2605: "★" BLACK STAR
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index c4f9601..9a96530 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -174,6 +174,7 @@
     public static final int CODE_SLASH = '/';
     public static final int CODE_COMMERCIAL_AT = '@';
     public static final int CODE_PLUS = '+';
+    public static final int CODE_PERCENT = '%';
     public static final int CODE_CLOSING_PARENTHESIS = ')';
     public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
     public static final int CODE_CLOSING_CURLY_BRACKET = '}';
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ccdbd0d..46b7512 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1503,6 +1503,7 @@
                 || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
                 || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET
                 || codePoint == Constants.CODE_PLUS
+                || codePoint == Constants.CODE_PERCENT
                 || Character.getType(codePoint) == Character.OTHER_SYMBOL;
     }
 
@@ -2305,9 +2306,9 @@
             if (!mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) {
                 mLastSelectionStart = mRecapitalizeStatus.getNewCursorStart();
                 mLastSelectionEnd = mRecapitalizeStatus.getNewCursorEnd();
-                mConnection.setSelection(mLastSelectionStart, mLastSelectionEnd);
             }
         }
+        mConnection.finishComposingText();
         mRecapitalizeStatus.rotate();
         final int numCharsDeleted = mLastSelectionEnd - mLastSelectionStart;
         mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
diff --git a/java/src/com/android/inputmethod/latin/about/AboutPreferences.java b/java/src/com/android/inputmethod/latin/about/AboutPreferences.java
new file mode 100644
index 0000000..f60b189
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/about/AboutPreferences.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.about;
+
+import android.app.Fragment;
+
+/**
+ * Dummy class of AboutPreferences. Never use this.
+ */
+public final class AboutPreferences extends Fragment {
+    private AboutPreferences() {
+        // Prevents this from being instantiated
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
index 9f7f502..fda97da 100644
--- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
@@ -60,7 +60,8 @@
                         0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
                         0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
                         new FormatOptions(version,
-                                0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE)));
+                                0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE),
+                                0 != (optionsFlags & FormatSpec.CONTAINS_TIMESTAMP_FLAG)));
         return header;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 5a5d7af..605930a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -37,13 +37,15 @@
      * sion
      *
      * o |
-     * p | not used                                4 bits
-     * t | has bigrams ?                           1 bit, 1 = yes, 0 = no : CONTAINS_BIGRAMS_FLAG
-     * i | FRENCH_LIGATURE_PROCESSING_FLAG
-     * o | supports dynamic updates ?              1 bit, 1 = yes, 0 = no : SUPPORTS_DYNAMIC_UPDATE
-     * n | GERMAN_UMLAUT_PROCESSING_FLAG
-     * f |
-     * lags
+     * p | not used                                3 bits
+     * t | each unigram and bigram entry has a time stamp?
+     * i |                                         1 bit, 1 = yes, 0 = no : CONTAINS_TIMESTAMP_FLAG
+     * o | has bigrams ?                           1 bit, 1 = yes, 0 = no : CONTAINS_BIGRAMS_FLAG
+     * n | FRENCH_LIGATURE_PROCESSING_FLAG
+     * f | supports dynamic updates ?              1 bit, 1 = yes, 0 = no : SUPPORTS_DYNAMIC_UPDATE
+     * l | GERMAN_UMLAUT_PROCESSING_FLAG
+     * a |
+     * gs
      *
      * h |
      * e | size of the file header, 4bytes
@@ -211,6 +213,8 @@
     static final int SUPPORTS_DYNAMIC_UPDATE = 0x2;
     static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
     static final int CONTAINS_BIGRAMS_FLAG = 0x8;
+    // TODO: Implement timestamps for unigram.
+    static final int CONTAINS_TIMESTAMP_FLAG = 0x10;
 
     // TODO: Make this value adaptative to content data, store it in the header, and
     // use it in the reading code.
@@ -276,9 +280,14 @@
     // is 584KB with the block size being 4.
     // This is 91% of that of full address table.
     static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4;
-    static final int BIGRAM_CONTENT_COUNT = 1;
+    static final int BIGRAM_CONTENT_COUNT = 2;
     static final int BIGRAM_FREQ_CONTENT_INDEX = 0;
+    static final int BIGRAM_TIMESTAMP_CONTENT_INDEX = 1;
     static final String BIGRAM_FREQ_CONTENT_ID = "_freq";
+    static final String BIGRAM_TIMESTAMP_CONTENT_ID = "_timestamp";
+    static final int BIGRAM_TIMESTAMP_SIZE = 4;
+    static final int BIGRAM_COUNTER_SIZE = 1;
+    static final int BIGRAM_LEVEL_SIZE = 1;
 
     static final int SHORTCUT_CONTENT_COUNT = 1;
     static final int SHORTCUT_CONTENT_INDEX = 0;
@@ -321,6 +330,7 @@
         public final int mVersion;
         public final boolean mSupportsDynamicUpdate;
         public final boolean mHasTerminalId;
+        public final boolean mHasTimestamp;
         @UsedForTesting
         public FormatOptions(final int version) {
             this(version, false);
@@ -328,6 +338,11 @@
 
         @UsedForTesting
         public FormatOptions(final int version, final boolean supportsDynamicUpdate) {
+            this(version, supportsDynamicUpdate, false /* hasTimestamp */);
+        }
+
+        public FormatOptions(final int version, final boolean supportsDynamicUpdate,
+                final boolean hasTimestamp) {
             mVersion = version;
             if (version < FIRST_VERSION_WITH_DYNAMIC_UPDATE && supportsDynamicUpdate) {
                 throw new RuntimeException("Dynamic updates are only supported with versions "
@@ -335,6 +350,7 @@
             }
             mSupportsDynamicUpdate = supportsDynamicUpdate;
             mHasTerminalId = (version >= FIRST_VERSION_WITH_TERMINAL_ID);
+            mHasTimestamp = hasTimestamp;
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index 5372907..734223e 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -153,8 +153,12 @@
         final File contentFile = new File(mDictDirectory, mDictDirectory.getName()
                 + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX
                 + FormatSpec.SHORTCUT_CONTENT_ID);
+        final File timestampsFile = new File(mDictDirectory, mDictDirectory.getName()
+                + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX
+                + FormatSpec.SHORTCUT_CONTENT_ID);
         mShortcutAddressTable = SparseTable.readFromFiles(lookupIndexFile,
-                new File[] { contentFile }, FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE);
+                new File[] { contentFile, timestampsFile },
+                FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE);
     }
 
     protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader {
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index f9dcacf..fe98942 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -69,16 +69,16 @@
         private final File[] mContentFiles;
         protected final OutputStream[] mContentOutStreams;
 
-        public SparseTableContentWriter(final String name, final int contentCount,
-                final int initialCapacity, final int blockSize, final File baseDir,
-                final String[] contentFilenames, final String[] contentIds) {
+        public SparseTableContentWriter(final String name, final int initialCapacity,
+                final int blockSize, final File baseDir, final String[] contentFilenames,
+                final String[] contentIds) {
             if (contentFilenames.length != contentIds.length) {
                 throw new RuntimeException("The length of contentFilenames and the length of"
                         + " contentIds are different " + contentFilenames.length + ", "
                         + contentIds.length);
             }
-            mContentCount = contentCount;
-            mSparseTable = new SparseTable(initialCapacity, blockSize, contentCount);
+            mContentCount = contentFilenames.length;
+            mSparseTable = new SparseTable(initialCapacity, blockSize, mContentCount);
             mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
             mAddressTableFiles = new File[mContentCount];
             mContentFiles = new File[mContentCount];
@@ -113,16 +113,40 @@
     }
 
     private static class BigramContentWriter extends SparseTableContentWriter {
+        private final boolean mWriteTimestamp;
 
         public BigramContentWriter(final String name, final int initialCapacity,
-                final File baseDir) {
-            super(name + FormatSpec.BIGRAM_FILE_EXTENSION, FormatSpec.BIGRAM_CONTENT_COUNT,
-                    initialCapacity, FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
-                    new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION },
-                    new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID });
+                final File baseDir, final boolean writeTimestamp) {
+            super(name + FormatSpec.BIGRAM_FILE_EXTENSION, initialCapacity,
+                    FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
+                    getContentFilenames(name, writeTimestamp), getContentIds(writeTimestamp));
+            mWriteTimestamp = writeTimestamp;
         }
 
-        public void writeBigramsForOneWord(final int terminalId,
+        private static String[] getContentFilenames(final String name,
+                final boolean writeTimestamp) {
+            final String[] contentFilenames;
+            if (writeTimestamp) {
+                contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION,
+                        name + FormatSpec.BIGRAM_FILE_EXTENSION };
+            } else {
+                contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION };
+            }
+            return contentFilenames;
+        }
+
+        private static String[] getContentIds(final boolean writeTimestamp) {
+            final String[] contentIds;
+            if (writeTimestamp) {
+                contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID,
+                        FormatSpec.BIGRAM_TIMESTAMP_CONTENT_ID };
+            } else {
+                contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID };
+            }
+            return contentIds;
+        }
+
+        public void writeBigramsForOneWord(final int terminalId, final int bigramCount,
                 final Iterator<WeightedString> bigramIterator, final FusionDictionary dict)
                         throws IOException {
             write(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId,
@@ -130,8 +154,16 @@
                         @Override
                         public void write(final OutputStream outStream) throws IOException {
                             writeBigramsForOneWordInternal(outStream, bigramIterator, dict);
-                        }
-            });
+                        }});
+            if (mWriteTimestamp) {
+                write(FormatSpec.BIGRAM_TIMESTAMP_CONTENT_INDEX, terminalId,
+                        new SparseTableContentWriterInterface() {
+                            @Override
+                            public void write(final OutputStream outStream) throws IOException {
+                                initBigramTimestampsCountersAndLevelsForOneWordInternal(outStream,
+                                        bigramCount);
+                            }});
+            }
         }
 
         private void writeBigramsForOneWordInternal(final OutputStream outStream,
@@ -151,13 +183,26 @@
                         FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE);
             }
         }
+
+        private void initBigramTimestampsCountersAndLevelsForOneWordInternal(
+                final OutputStream outStream, final int bigramCount) throws IOException {
+            for (int i = 0; i < bigramCount; ++i) {
+                // TODO: Figure out what initial values should be.
+                BinaryDictEncoderUtils.writeUIntToStream(outStream, 0 /* value */,
+                        FormatSpec.BIGRAM_TIMESTAMP_SIZE);
+                BinaryDictEncoderUtils.writeUIntToStream(outStream, 0 /* value */,
+                        FormatSpec.BIGRAM_COUNTER_SIZE);
+                BinaryDictEncoderUtils.writeUIntToStream(outStream, 0 /* value */,
+                        FormatSpec.BIGRAM_LEVEL_SIZE);
+            }
+        }
     }
 
     private static class ShortcutContentWriter extends SparseTableContentWriter {
         public ShortcutContentWriter(final String name, final int initialCapacity,
                 final File baseDir) {
-            super(name + FormatSpec.SHORTCUT_FILE_EXTENSION, FormatSpec.SHORTCUT_CONTENT_COUNT,
-                    initialCapacity, FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
+            super(name + FormatSpec.SHORTCUT_FILE_EXTENSION, initialCapacity,
+                    FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
                     new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION },
                     new String[] { FormatSpec.SHORTCUT_CONTENT_ID });
         }
@@ -257,7 +302,8 @@
         if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
 
         writeTerminalData(flatNodes, terminalCount);
-        mBigramWriter = new BigramContentWriter(mBaseFilename, terminalCount, mDictDir);
+        mBigramWriter = new BigramContentWriter(mBaseFilename, terminalCount, mDictDir,
+                formatOptions.mHasTimestamp);
         writeBigrams(flatNodes, dict);
         mShortcutWriter = new ShortcutContentWriter(mBaseFilename, terminalCount, mDictDir);
         writeShortcuts(flatNodes);
@@ -348,7 +394,7 @@
         for (final PtNodeArray nodeArray : flatNodes) {
             for (final PtNode ptNode : nodeArray.mData) {
                 if (ptNode.mBigrams != null) {
-                    mBigramWriter.writeBigramsForOneWord(ptNode.mTerminalId,
+                    mBigramWriter.writeBigramsForOneWord(ptNode.mTerminalId, ptNode.mBigrams.size(),
                             ptNode.mBigrams.iterator(), dict);
                 }
             }
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java
index ef6ab2a..a23e377 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java
@@ -21,6 +21,7 @@
 import android.preference.PreferenceActivity;
 
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.utils.FragmentUtils;
 
 public final class DebugSettingsActivity extends PreferenceActivity {
     private static final String DEFAULT_FRAGMENT = DebugSettings.class.getName();
@@ -42,6 +43,6 @@
     // TODO: Uncomment the override annotation once we start using SDK version 19.
     // @Override
     public boolean isValidFragment(String fragmentName) {
-        return fragmentName.equals(DEFAULT_FRAGMENT);
+        return FragmentUtils.isValidFragment(fragmentName);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 1a0fecc..dc005bb 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -101,6 +101,7 @@
     // Emoji
     public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys";
     public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id";
+    public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id";
 
     private Resources mRes;
     private SharedPreferences mPrefs;
@@ -383,15 +384,25 @@
         return prefs.getString(PREF_EMOJI_RECENT_KEYS, "");
     }
 
-    public static void writeEmojiCategoryLastTypedId(
-            final SharedPreferences prefs, final int category, final int id) {
-        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + category;
-        prefs.edit().putInt(key, id).apply();
+    public static void writeLastTypedEmojiCategoryPageId(
+            final SharedPreferences prefs, final int categoryId, final int categoryPageId) {
+        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
+        prefs.edit().putInt(key, categoryPageId).apply();
     }
 
-    public static int readEmojiCategoryLastTypedId(
-            final SharedPreferences prefs, final int category) {
-        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + category;
+    public static int readLastTypedEmojiCategoryPageId(
+            final SharedPreferences prefs, final int categoryId) {
+        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
         return prefs.getInt(key, 0);
     }
+
+    public static void writeLastShownEmojiCategoryId(
+            final SharedPreferences prefs, final int categoryId) {
+        prefs.edit().putInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, categoryId).apply();
+    }
+
+    public static int readLastShownEmojiCategoryId(
+            final SharedPreferences prefs, final int defValue) {
+        return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, defValue);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
index ad68f8c..c899507 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.settings;
 
+import com.android.inputmethod.latin.utils.FragmentUtils;
+
 import android.content.Intent;
 import android.preference.PreferenceActivity;
 
@@ -36,6 +38,6 @@
     // TODO: Uncomment the override annotation once we start using SDK version 19.
     // @Override
     public boolean isValidFragment(String fragmentName) {
-        return fragmentName.equals(DEFAULT_FRAGMENT);
+        return FragmentUtils.isValidFragment(fragmentName);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
index aba5637..df9a761 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.spellcheck;
 
+import com.android.inputmethod.latin.utils.FragmentUtils;
+
 import android.content.Intent;
 import android.os.Bundle;
 import android.preference.PreferenceActivity;
@@ -42,6 +44,6 @@
     // TODO: Uncomment the override annotation once we start using SDK version 19.
     // @Override
     public boolean isValidFragment(String fragmentName) {
-        return fragmentName.equals(DEFAULT_FRAGMENT);
+        return FragmentUtils.isValidFragment(fragmentName);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index ff332cd..d87f6f3 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -25,6 +25,7 @@
 import android.text.TextUtils;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 
@@ -143,12 +144,17 @@
         // from the current users. So, you should be really careful to change it.
         final int subtypeId = getInputMethodSubtypeId(nameId, localeString, layoutExtraValue,
                 additionalSubtypeExtraValue);
-        // TODO: Use InputMethodSubtypeBuilder once we use SDK version 19.
-        return new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark,
-                localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue
-                        + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
-                        + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false,
-                        subtypeId);
+        final String extraValue;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            extraValue = layoutExtraValue + "," + additionalSubtypeExtraValue
+                    + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+                    + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
+        } else {
+            extraValue = layoutExtraValue + "," + additionalSubtypeExtraValue;
+        }
+        return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId,
+                R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, extraValue,
+                false, false, subtypeId);
     }
 
     private static int getInputMethodSubtypeId(int nameId, String localeString,
diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
new file mode 100644
index 0000000..ee2b97b
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import com.android.inputmethod.dictionarypack.DictionarySettingsFragment;
+import com.android.inputmethod.latin.about.AboutPreferences;
+import com.android.inputmethod.latin.settings.AdditionalSubtypeSettings;
+import com.android.inputmethod.latin.settings.DebugSettings;
+import com.android.inputmethod.latin.settings.SettingsFragment;
+import com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment;
+import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordFragment;
+import com.android.inputmethod.latin.userdictionary.UserDictionaryList;
+import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker;
+import com.android.inputmethod.latin.userdictionary.UserDictionarySettings;
+import com.android.inputmethod.research.FeedbackFragment;
+
+import java.util.HashSet;
+
+public class FragmentUtils {
+    private static final HashSet<String> sLatinImeFragments = new HashSet<String>();
+    static {
+        sLatinImeFragments.add(DictionarySettingsFragment.class.getName());
+        sLatinImeFragments.add(AboutPreferences.class.getName());
+        sLatinImeFragments.add(AdditionalSubtypeSettings.class.getName());
+        sLatinImeFragments.add(DebugSettings.class.getName());
+        sLatinImeFragments.add(SettingsFragment.class.getName());
+        sLatinImeFragments.add(SpellCheckerSettingsFragment.class.getName());
+        sLatinImeFragments.add(UserDictionaryAddWordFragment.class.getName());
+        sLatinImeFragments.add(UserDictionaryList.class.getName());
+        sLatinImeFragments.add(UserDictionaryLocalePicker.class.getName());
+        sLatinImeFragments.add(UserDictionarySettings.class.getName());
+        sLatinImeFragments.add(FeedbackFragment.class.getName());
+    }
+
+    public static boolean isValidFragment(String fragmentName) {
+        return sLatinImeFragments.contains(fragmentName);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 0189b33..32c07e1 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -80,6 +80,9 @@
             new FormatSpec.FormatOptions(4, false /* supportsDynamicUpdate */);
     private static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE =
             new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */);
+    private static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP =
+            new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */,
+                    true /* hasTimestamp */);
 
     private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
 
@@ -363,6 +366,7 @@
         runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
         runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
         runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -377,6 +381,7 @@
         runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
         runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
         runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -508,6 +513,8 @@
         runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
         runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
         runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER,
+                VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -522,6 +529,8 @@
         runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
         runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
         runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY,
+                VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
 
         for (final String result : results) {
             Log.d(TAG, result);
@@ -634,12 +643,14 @@
         runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
         runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
         runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
 
         runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION2);
         runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
         runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
         runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
         runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP);
 
         for (final String result : results) {
             Log.d(TAG, result);
diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
index 39ecfab..64396b1 100644
--- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
@@ -77,7 +77,7 @@
     <string name="more_keys_for_currency_dollar">&#x00A2;,&#x00A3;,&#x20AC;,&#x00A5;,&#x20B1;</string>
     <string name="keylabel_for_currency">$</string>
     <string name="more_keys_for_currency">$,&#x00A2;,&#x20AC;,&#x00A3;,&#x00A5;,&#x20B1;</string>
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!3,!,\\,,\?,:,;,\@"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!4,#,!,\\,,\?,-,:,',\@"</string>
     <!-- U+2020: "†" DAGGER
          U+2021: "‡" DOUBLE DAGGER
          U+2605: "★" BLACK STAR -->