Merge "Fix auto-detection of format 4."
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index f7c54c7..e4051e8 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -50,15 +50,15 @@
 import com.android.inputmethod.annotations.ExternallyReferenced;
 import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.keyboard.internal.DrawingHandler;
 import com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText;
 import com.android.inputmethod.keyboard.internal.GestureTrailsPreview;
 import com.android.inputmethod.keyboard.internal.KeyDrawParams;
 import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
-import com.android.inputmethod.keyboard.internal.MainKeyboardViewDrawingHandler;
-import com.android.inputmethod.keyboard.internal.MainKeyboardViewTimerHandler;
 import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
 import com.android.inputmethod.keyboard.internal.PreviewPlacerView;
 import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview;
+import com.android.inputmethod.keyboard.internal.TimerHandler;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
@@ -120,7 +120,8 @@
  * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
  */
 public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
-        PointerTracker.DrawingProxy, MoreKeysPanel.Controller {
+        PointerTracker.DrawingProxy, MoreKeysPanel.Controller, DrawingHandler.Callbacks,
+        TimerHandler.Callbacks {
     private static final String TAG = MainKeyboardView.class.getSimpleName();
 
     /** Listener for {@link KeyboardActionListener}. */
@@ -196,14 +197,14 @@
     // TODO: Make this parameter customizable by user via settings.
     private int mGestureFloatingPreviewTextLingerTimeout;
 
-    private KeyDetector mKeyDetector;
+    private final KeyDetector mKeyDetector;
     private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
 
-    private final MainKeyboardViewTimerHandler mKeyTimerHandler;
+    private final TimerHandler mKeyTimerHandler;
     private final int mLanguageOnSpacebarHorizontalMargin;
 
-    private final MainKeyboardViewDrawingHandler mDrawingHandler =
-            new MainKeyboardViewDrawingHandler(this);
+    private final DrawingHandler mDrawingHandler =
+            new DrawingHandler(this);
 
     public MainKeyboardView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.mainKeyboardViewStyle);
@@ -256,7 +257,12 @@
                 R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
         mKeyDetector = new KeyDetector(
                 keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
-        mKeyTimerHandler = new MainKeyboardViewTimerHandler(this, mainKeyboardViewAttr);
+        final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
+                R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
+        final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
+                R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
+        mKeyTimerHandler = new TimerHandler(
+                this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime);
         mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
                 R.styleable.MainKeyboardView_keyPreviewOffset, 0);
         mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
@@ -344,11 +350,14 @@
         animatorToStart.setCurrentPlayTime(startTime);
     }
 
+    // Implements {@link TimerHander.Callbacks} method.
+    @Override
     public void startWhileTypingFadeinAnimation() {
         cancelAndStartAnimators(
                 mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
     }
 
+    @Override
     public void startWhileTypingFadeoutAnimation() {
         cancelAndStartAnimators(
                 mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
@@ -521,6 +530,8 @@
         return previewTextView;
     }
 
+    // Implements {@link DrawingHandler.Callbacks} method.
+    @Override
     public void dismissAllKeyPreviews() {
         for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) {
             dismissKeyPreviewWithoutDelay(key);
@@ -714,7 +725,9 @@
         return zoomOutAnimation;
     }
 
+    // Implements {@link TimerHandler.Callbacks} method.
     // TODO: Take this method out of this class.
+    @Override
     public void dismissKeyPreviewWithoutDelay(final Key key) {
         if (key == null) {
             return;
@@ -773,6 +786,8 @@
         mGestureTrailsPreview.setPreviewEnabled(isGestureTrailEnabled);
     }
 
+    // Implements {@link DrawingHandler.Callbacks} method.
+    @Override
     public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
         locatePreviewPlacerView();
         mGestureFloatingPreviewText.setSuggetedWords(suggestedWords);
@@ -848,10 +863,12 @@
         return moreKeysKeyboardView;
     }
 
+    // Implements {@link TimerHandler.Callbacks} method.
     /**
      * Called when a key is long pressed.
      * @param tracker the pointer tracker which pressed the parent key
      */
+    @Override
     public void onLongPress(final PointerTracker tracker) {
         if (isShowingMoreKeysPanel()) {
             return;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index ad5aedf..39fb0b5 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -195,7 +195,7 @@
     private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER;
 
     private Keyboard mKeyboard;
-    private int mPhantonSuddenMoveThreshold;
+    private int mPhantomSuddenMoveThreshold;
     private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
 
     private boolean mIsDetectingGesture = false; // per PointerTracker.
@@ -446,8 +446,6 @@
         for (int i = 0; i < trackersSize; ++i) {
             final PointerTracker tracker = sTrackers.get(i);
             tracker.setKeyDetectorInner(keyDetector);
-            // Mark that keyboard layout has been changed.
-            tracker.mKeyboardLayoutHasBeenChanged = true;
         }
         final Keyboard keyboard = keyDetector.getKeyboard();
         sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
@@ -458,7 +456,7 @@
         final int trackersSize = sTrackers.size();
         for (int i = 0; i < trackersSize; ++i) {
             final PointerTracker tracker = sTrackers.get(i);
-            tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
+            tracker.setReleasedKeyGraphics(tracker.getKey());
         }
     }
 
@@ -466,10 +464,7 @@
         final int trackersSize = sTrackers.size();
         for (int i = 0; i < trackersSize; ++i) {
             final PointerTracker tracker = sTrackers.get(i);
-            if (tracker.isShowingMoreKeysPanel()) {
-                tracker.mMoreKeysPanel.dismissMoreKeysPanel();
-                tracker.mMoreKeysPanel = null;
-            }
+            tracker.dismissMoreKeysPanel();
         }
     }
 
@@ -604,6 +599,8 @@
         }
         mKeyDetector = keyDetector;
         mKeyboard = keyDetector.getKeyboard();
+        // Mark that keyboard layout has been changed.
+        mKeyboardLayoutHasBeenChanged = true;
         final int keyWidth = mKeyboard.mMostCommonKeyWidth;
         final int keyHeight = mKeyboard.mMostCommonKeyHeight;
         mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
@@ -614,7 +611,7 @@
             }
             // Keep {@link #mCurrentKey} that comes from previous keyboard.
         }
-        mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
+        mPhantomSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
         mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
     }
 
@@ -958,6 +955,13 @@
         return (mMoreKeysPanel != null);
     }
 
+    private void dismissMoreKeysPanel() {
+        if (isShowingMoreKeysPanel()) {
+            mMoreKeysPanel.dismissMoreKeysPanel();
+            mMoreKeysPanel = null;
+        }
+    }
+
     private void onDownEventInternal(final int x, final int y, final long eventTime) {
         Key key = onDownKey(x, y, eventTime);
         // Key selection by dragging finger is allowed when 1) key selection by dragging finger is
@@ -1134,7 +1138,7 @@
         // successive up and down events.
         // TODO: Should find a way to balance gesture detection and this hack.
         else if (sNeedsPhantomSuddenMoveEventHack
-                && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
+                && getDistance(x, y, lastX, lastY) >= mPhantomSuddenMoveThreshold) {
             processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
         }
         // HACK: On some devices, quick successive proximate touches may be reported as a bogus
@@ -1382,28 +1386,28 @@
         if (key == null) return;
         if (!key.isLongPressEnabled()) return;
         // Caveat: Please note that isLongPressEnabled() can be true even if the current key
-        // doesn't have its more keys. (e.g. spacebar, globe key)
+        // doesn't have its more keys. (e.g. spacebar, globe key) If we are in the dragging finger
+        // mode, we will disable long press timer of such key.
         // We always need to start the long press timer if the key has its more keys regardless of
         // whether or not we are in the dragging finger mode.
         if (mIsInDraggingFinger && key.getMoreKeys() == null) return;
-        final int delay;
-        switch (key.getCode()) {
-        case Constants.CODE_SHIFT:
-            delay = sParams.mLongPressShiftLockTimeout;
-            break;
-        default:
-            final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
-            if (mIsInSlidingKeyInput) {
-                // We use longer timeout for sliding finger input started from the modifier key.
-                delay = longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
-            } else {
-                delay = longpressTimeout;
-            }
-            break;
-        }
+
+        final int delay = getLongPressTimeout(key.getCode());
         mTimerProxy.startLongPressTimer(this, delay);
     }
 
+    private int getLongPressTimeout(final int code) {
+        if (code == Constants.CODE_SHIFT) {
+            return sParams.mLongPressShiftLockTimeout;
+        }
+        final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
+        if (mIsInSlidingKeyInput) {
+            // We use longer timeout for sliding finger input started from the modifier key.
+            return longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
+        }
+        return longpressTimeout;
+    }
+
     private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
         if (key == null) {
             callListenerOnCancelInput();
@@ -1422,7 +1426,7 @@
         // Don't start key repeat when we are in the dragging finger mode.
         if (mIsInDraggingFinger) return;
         final int startRepeatCount = 1;
-        mTimerProxy.startKeyRepeatTimer(this, startRepeatCount, sParams.mKeyRepeatStartTimeout);
+        startKeyRepeatTimer(startRepeatCount);
     }
 
     public void onKeyRepeat(final int code, final int repeatCount) {
@@ -1434,11 +1438,17 @@
         mCurrentRepeatingKeyCode = code;
         mIsDetectingGesture = false;
         final int nextRepeatCount = repeatCount + 1;
-        mTimerProxy.startKeyRepeatTimer(this, nextRepeatCount, sParams.mKeyRepeatInterval);
+        startKeyRepeatTimer(nextRepeatCount);
         callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
         callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis());
     }
 
+    private void startKeyRepeatTimer(final int repeatCount) {
+        final int delay =
+                (repeatCount == 1) ? sParams.mKeyRepeatStartTimeout : sParams.mKeyRepeatInterval;
+        mTimerProxy.startKeyRepeatTimer(this, repeatCount, delay);
+    }
+
     private void printTouchEvent(final String title, final int x, final int y,
             final long eventTime) {
         final Key key = mKeyDetector.detectHitKey(x, y);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MainKeyboardViewDrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
similarity index 62%
rename from java/src/com/android/inputmethod/keyboard/internal/MainKeyboardViewDrawingHandler.java
rename to java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
index 5384553..df82bec 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MainKeyboardViewDrawingHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
@@ -19,28 +19,37 @@
 import android.os.Message;
 
 import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks;
 import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
+import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 
-public class MainKeyboardViewDrawingHandler extends StaticInnerHandlerWrapper<MainKeyboardView> {
+// TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so.
+public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> {
+    public interface Callbacks {
+        public void dismissKeyPreviewWithoutDelay(Key key);
+        public void dismissAllKeyPreviews();
+        public void showGestureFloatingPreviewText(SuggestedWords suggestedWords);
+    }
+
     private static final int MSG_DISMISS_KEY_PREVIEW = 0;
     private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
 
-    public MainKeyboardViewDrawingHandler(final MainKeyboardView outerInstance) {
-        super(outerInstance);
+    public DrawingHandler(final Callbacks ownerInstance) {
+        super(ownerInstance);
     }
 
     @Override
     public void handleMessage(final Message msg) {
-        final MainKeyboardView mainKeyboardView = getOuterInstance();
-        if (mainKeyboardView == null) return;
+        final Callbacks callbacks = getOwnerInstance();
+        if (callbacks == null) {
+            return;
+        }
         switch (msg.what) {
         case MSG_DISMISS_KEY_PREVIEW:
-            mainKeyboardView.dismissKeyPreviewWithoutDelay((Key)msg.obj);
+            callbacks.dismissKeyPreviewWithoutDelay((Key)msg.obj);
             break;
         case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
-            mainKeyboardView.showGestureFloatingPreviewText(SuggestedWords.EMPTY);
+            callbacks.showGestureFloatingPreviewText(SuggestedWords.EMPTY);
             break;
         }
     }
@@ -51,9 +60,11 @@
 
     private void cancelAllDismissKeyPreviews() {
         removeMessages(MSG_DISMISS_KEY_PREVIEW);
-        final MainKeyboardView mainKeyboardView = getOuterInstance();
-        if (mainKeyboardView == null) return;
-        mainKeyboardView.dismissAllKeyPreviews();
+        final Callbacks callbacks = getOwnerInstance();
+        if (callbacks == null) {
+            return;
+        }
+        callbacks.dismissAllKeyPreviews();
     }
 
     public void dismissGestureFloatingPreviewText(final long delay) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
index 19e9955..8b413e5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
@@ -31,7 +31,7 @@
 import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.keyboard.internal.GestureTrail.Params;
 import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
+import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 
 /**
  * Draw gesture trail preview graphics during gesture.
@@ -52,21 +52,23 @@
     private final DrawingHandler mDrawingHandler;
 
     private static final class DrawingHandler
-            extends StaticInnerHandlerWrapper<GestureTrailsPreview> {
+            extends LeakGuardHandlerWrapper<GestureTrailsPreview> {
         private static final int MSG_UPDATE_GESTURE_TRAIL = 0;
 
         private final Params mGestureTrailParams;
 
-        public DrawingHandler(final GestureTrailsPreview outerInstance,
+        public DrawingHandler(final GestureTrailsPreview ownerInstance,
                 final Params gestureTrailParams) {
-            super(outerInstance);
+            super(ownerInstance);
             mGestureTrailParams = gestureTrailParams;
         }
 
         @Override
         public void handleMessage(final Message msg) {
-            final GestureTrailsPreview preview = getOuterInstance();
-            if (preview == null) return;
+            final GestureTrailsPreview preview = getOwnerInstance();
+            if (preview == null) {
+                return;
+            }
             switch (msg.what) {
             case MSG_UPDATE_GESTURE_TRAIL:
                 preview.getDrawingView().invalidate();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MainKeyboardViewTimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
similarity index 80%
rename from java/src/com/android/inputmethod/keyboard/internal/MainKeyboardViewTimerHandler.java
rename to java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
index 3ffb2b6..966cb95 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MainKeyboardViewTimerHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
@@ -16,21 +16,25 @@
 
 package com.android.inputmethod.keyboard.internal;
 
-import android.content.res.TypedArray;
 import android.os.Message;
 import android.os.SystemClock;
 import android.view.ViewConfiguration;
 
 import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks;
 import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
+import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 
-public final class MainKeyboardViewTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
-        implements TimerProxy {
+// TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so.
+public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy {
+    public interface Callbacks {
+        public void startWhileTypingFadeinAnimation();
+        public void startWhileTypingFadeoutAnimation();
+        public void onLongPress(PointerTracker tracker);
+    }
+
     private static final int MSG_TYPING_STATE_EXPIRED = 0;
     private static final int MSG_REPEAT_KEY = 1;
     private static final int MSG_LONGPRESS_KEY = 2;
@@ -40,32 +44,29 @@
     private final int mIgnoreAltCodeKeyTimeout;
     private final int mGestureRecognitionUpdateTime;
 
-    public MainKeyboardViewTimerHandler(final MainKeyboardView outerInstance,
-            final TypedArray mainKeyboardViewAttr) {
-        super(outerInstance);
-
-        mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
-                R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
-        mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
-                R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
+    public TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout,
+            final int gestureRecognitionUpdateTime) {
+        super(ownerInstance);
+        mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout;
+        mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime;
     }
 
     @Override
     public void handleMessage(final Message msg) {
-        final MainKeyboardView keyboardView = getOuterInstance();
-        if (keyboardView == null) {
+        final Callbacks callbacks = getOwnerInstance();
+        if (callbacks == null) {
             return;
         }
         final PointerTracker tracker = (PointerTracker) msg.obj;
         switch (msg.what) {
         case MSG_TYPING_STATE_EXPIRED:
-            keyboardView.startWhileTypingFadeinAnimation();
+            callbacks.startWhileTypingFadeinAnimation();
             break;
         case MSG_REPEAT_KEY:
             tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
             break;
         case MSG_LONGPRESS_KEY:
-            keyboardView.onLongPress(tracker);
+            callbacks.onLongPress(tracker);
             break;
         case MSG_UPDATE_BATCH_INPUT:
             tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
@@ -114,13 +115,16 @@
 
         final boolean isTyping = isTypingState();
         removeMessages(MSG_TYPING_STATE_EXPIRED);
-        final MainKeyboardView keyboardView = getOuterInstance();
+        final Callbacks callbacks = getOwnerInstance();
+        if (callbacks == null) {
+            return;
+        }
 
         // When user hits the space or the enter key, just cancel the while-typing timer.
         final int typedCode = typedKey.getCode();
         if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
             if (isTyping) {
-                keyboardView.startWhileTypingFadeinAnimation();
+                callbacks.startWhileTypingFadeinAnimation();
             }
             return;
         }
@@ -130,7 +134,7 @@
         if (isTyping) {
             return;
         }
-        keyboardView.startWhileTypingFadeoutAnimation();
+        callbacks.startWhileTypingFadeoutAnimation();
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6445b61..87eed3b 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -97,8 +97,8 @@
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
+import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 import com.android.inputmethod.latin.utils.RecapitalizeStatus;
-import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
 import com.android.inputmethod.latin.utils.StringUtils;
 import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
 import com.android.inputmethod.latin.utils.TextRange;
@@ -224,7 +224,7 @@
     public final UIHandler mHandler = new UIHandler(this);
     private InputUpdater mInputUpdater;
 
-    public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
+    public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
         private static final int MSG_UPDATE_SHIFT_STATE = 0;
         private static final int MSG_PENDING_IMS_CALLBACK = 1;
         private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
@@ -247,12 +247,12 @@
         private long mDoubleSpacePeriodTimeout;
         private long mDoubleSpacePeriodTimerStart;
 
-        public UIHandler(final LatinIME outerInstance) {
-            super(outerInstance);
+        public UIHandler(final LatinIME ownerInstance) {
+            super(ownerInstance);
         }
 
         public void onCreate() {
-            final Resources res = getOuterInstance().getResources();
+            final Resources res = getOwnerInstance().getResources();
             mDelayUpdateSuggestions =
                     res.getInteger(R.integer.config_delay_update_suggestions);
             mDelayUpdateShiftState =
@@ -263,7 +263,7 @@
 
         @Override
         public void handleMessage(final Message msg) {
-            final LatinIME latinIme = getOuterInstance();
+            final LatinIME latinIme = getOwnerInstance();
             final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
             switch (msg.what) {
             case MSG_UPDATE_SUGGESTION_STRIP:
@@ -407,7 +407,7 @@
             removeMessages(MSG_PENDING_IMS_CALLBACK);
             resetPendingImsCallback();
             mIsOrientationChanging = true;
-            final LatinIME latinIme = getOuterInstance();
+            final LatinIME latinIme = getOwnerInstance();
             if (latinIme.isInputViewShown()) {
                 latinIme.mKeyboardSwitcher.saveKeyboardState();
             }
@@ -440,7 +440,7 @@
                     mIsOrientationChanging = false;
                     mPendingSuccessiveImsCallback = true;
                 }
-                final LatinIME latinIme = getOuterInstance();
+                final LatinIME latinIme = getOwnerInstance();
                 executePendingImsCallback(latinIme, editorInfo, restarting);
                 latinIme.onStartInputInternal(editorInfo, restarting);
             }
@@ -459,7 +459,7 @@
                     sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
                             PENDING_IMS_CALLBACK_DURATION);
                 }
-                final LatinIME latinIme = getOuterInstance();
+                final LatinIME latinIme = getOwnerInstance();
                 executePendingImsCallback(latinIme, editorInfo, restarting);
                 latinIme.onStartInputViewInternal(editorInfo, restarting);
                 mAppliedEditorInfo = editorInfo;
@@ -471,7 +471,7 @@
                 // Typically this is the first onFinishInputView after orientation changed.
                 mHasPendingFinishInputView = true;
             } else {
-                final LatinIME latinIme = getOuterInstance();
+                final LatinIME latinIme = getOwnerInstance();
                 latinIme.onFinishInputViewInternal(finishingInput);
                 mAppliedEditorInfo = null;
             }
@@ -482,7 +482,7 @@
                 // Typically this is the first onFinishInput after orientation changed.
                 mHasPendingFinishInput = true;
             } else {
-                final LatinIME latinIme = getOuterInstance();
+                final LatinIME latinIme = getOwnerInstance();
                 executePendingImsCallback(latinIme, null, false);
                 latinIme.onFinishInputInternal();
             }
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
index c4a813c..5072fab 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -38,7 +38,7 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.settings.SettingsActivity;
 import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
+import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
 
 import java.util.ArrayList;
 
@@ -74,21 +74,21 @@
     private SettingsPoolingHandler mHandler;
 
     private static final class SettingsPoolingHandler
-            extends StaticInnerHandlerWrapper<SetupWizardActivity> {
+            extends LeakGuardHandlerWrapper<SetupWizardActivity> {
         private static final int MSG_POLLING_IME_SETTINGS = 0;
         private static final long IME_SETTINGS_POLLING_INTERVAL = 200;
 
         private final InputMethodManager mImmInHandler;
 
-        public SettingsPoolingHandler(final SetupWizardActivity outerInstance,
+        public SettingsPoolingHandler(final SetupWizardActivity ownerInstance,
                 final InputMethodManager imm) {
-            super(outerInstance);
+            super(ownerInstance);
             mImmInHandler = imm;
         }
 
         @Override
         public void handleMessage(final Message msg) {
-            final SetupWizardActivity setupWizardActivity = getOuterInstance();
+            final SetupWizardActivity setupWizardActivity = getOwnerInstance();
             if (setupWizardActivity == null) {
                 return;
             }
diff --git a/java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
similarity index 60%
rename from java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java
rename to java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
index 44e5d17..8469c87 100644
--- a/java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java
+++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
@@ -21,22 +21,22 @@
 
 import java.lang.ref.WeakReference;
 
-public class StaticInnerHandlerWrapper<T> extends Handler {
-    private final WeakReference<T> mOuterInstanceRef;
+public class LeakGuardHandlerWrapper<T> extends Handler {
+    private final WeakReference<T> mOwnerInstanceRef;
 
-    public StaticInnerHandlerWrapper(final T outerInstance) {
-        this(outerInstance, Looper.myLooper());
+    public LeakGuardHandlerWrapper(final T ownerInstance) {
+        this(ownerInstance, Looper.myLooper());
     }
 
-    public StaticInnerHandlerWrapper(final T outerInstance, final Looper looper) {
+    public LeakGuardHandlerWrapper(final T ownerInstance, final Looper looper) {
         super(looper);
-        if (outerInstance == null) {
-            throw new NullPointerException("outerInstance is null");
+        if (ownerInstance == null) {
+            throw new NullPointerException("ownerInstance is null");
         }
-        mOuterInstanceRef = new WeakReference<T>(outerInstance);
+        mOwnerInstanceRef = new WeakReference<T>(ownerInstance);
     }
 
-    public T getOuterInstance() {
-        return mOuterInstanceRef.get();
+    public T getOwnerInstance() {
+        return mOwnerInstanceRef.get();
     }
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_gc_event_listeners.cpp
index 22d4f7a..c582a6e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_gc_event_listeners.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_gc_event_listeners.cpp
@@ -127,7 +127,8 @@
                     ptNodeParams->getHeadPos(), writingPos));
     mValidPtNodeCount++;
     // Writes current PtNode.
-    return mPtNodeWriter->writePtNodeAndAdvancePosition(ptNodeParams, &writingPos);
+    return mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(ptNodeParams,
+            0 /* timestamp */, &writingPos);
 }
 
 bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateAllPositionFields
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp
index 6019d76..0222080 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp
@@ -16,55 +16,72 @@
 
 #include "suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h"
 
-#include "suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h"
+#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
-#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 
 namespace latinime {
 
-int ProbabilityDictContent::getProbability(const int terminalId) const {
+void ProbabilityDictContent::getProbabilityEntry(const int terminalId,
+        ProbabilityEntry *const outProbabilityEntry) const {
     if (terminalId < 0 || terminalId >= mSize) {
-        return NOT_A_PROBABILITY;
+        outProbabilityEntry->setProbability(0 /* flags */, NOT_A_PROBABILITY);
+        AKLOGE("Terminal id (%d) is not in the probability dict content. mSize: %d", terminalId,
+                mSize);
+        return;
     }
-    const int probabilityFieldPos =
-            getEntryPos(terminalId) + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE;
-    return getBuffer()->readUint(Ver4DictConstants::PROBABILITY_SIZE, probabilityFieldPos);
+    const BufferWithExtendableBuffer *const buffer = getBuffer();
+    int entryPos = getEntryPos(terminalId);
+    const int flags = buffer->readUintAndAdvancePosition(
+            Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &entryPos);
+    const int probability = buffer->readUintAndAdvancePosition(
+            Ver4DictConstants::PROBABILITY_SIZE, &entryPos);
+    if (mHasHistoricalInfo) {
+        const int timestamp = buffer->readUintAndAdvancePosition(
+                Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &entryPos);
+        const int level = buffer->readUintAndAdvancePosition(
+                Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &entryPos);
+        const int count = buffer->readUintAndAdvancePosition(
+                Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &entryPos);
+        outProbabilityEntry->setProbabilityWithHistricalInfo(flags, probability, timestamp, level,
+                count);
+    } else {
+        outProbabilityEntry->setProbability(flags, probability);
+    }
 }
 
-bool ProbabilityDictContent::setProbability(const int terminalId, const int probability) {
+bool ProbabilityDictContent::setProbabilityEntry(const int terminalId,
+        const ProbabilityEntry *const probabilityEntry) {
     if (terminalId < 0) {
         return false;
     }
+    const int entryPos = getEntryPos(terminalId);
     if (terminalId >= mSize) {
+        ProbabilityEntry dummyEntry;
         // Write new entry.
         int writingPos = getBuffer()->getTailPosition();
-        while (writingPos <= getEntryPos(terminalId)) {
-            const int dummyFlags = 0;
-            if (!getWritableBuffer()->writeUintAndAdvancePosition(dummyFlags,
-                    Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &writingPos)) {
+        while (writingPos <= entryPos) {
+            // Fulfilling with dummy entries until writingPos.
+            if (!writeEntry(&dummyEntry, writingPos)) {
+                AKLOGE("Cannot write dummy entry. pos: %d, mSize: %d", writingPos, mSize);
                 return false;
             }
-            const int dummyProbability = 0;
-            if (!getWritableBuffer()->writeUintAndAdvancePosition(dummyProbability,
-                    Ver4DictConstants::PROBABILITY_SIZE, &writingPos)) {
-                return false;
-            }
+            writingPos += getEntrySize();
             mSize++;
         }
     }
-    const int probabilityWritingPos = getEntryPos(terminalId)
-            + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE;
-    return getWritableBuffer()->writeUint(probability,
-            Ver4DictConstants::PROBABILITY_SIZE, probabilityWritingPos);
+    return writeEntry(probabilityEntry, entryPos);
 }
 
 bool ProbabilityDictContent::flushToFile(const char *const dictDirPath) const {
     if (getEntryPos(mSize) < getBuffer()->getTailPosition()) {
-        ProbabilityDictContent probabilityDictContentToWrite;
+        ProbabilityDictContent probabilityDictContentToWrite(mHasHistoricalInfo);
+        ProbabilityEntry probabilityEntry;
         for (int i = 0; i < mSize; ++i) {
-            if (!probabilityDictContentToWrite.setProbability(i, getProbability(i))) {
+            getProbabilityEntry(i, &probabilityEntry);
+            if (!probabilityDictContentToWrite.setProbabilityEntry(i, &probabilityEntry)) {
+                AKLOGE("Cannot set probability entry in flushToFile. terminalId: %d", i);
                 return false;
             }
         }
@@ -79,10 +96,12 @@
         const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
         const ProbabilityDictContent *const originalProbabilityDictContent) {
     mSize = 0;
+    ProbabilityEntry probabilityEntry;
     for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin();
             it != terminalIdMap->end(); ++it) {
-        if (!setProbability(it->second,
-                originalProbabilityDictContent->getProbability(it->first))) {
+        originalProbabilityDictContent->getProbabilityEntry(it->first, &probabilityEntry);
+        if (!setProbabilityEntry(it->second, &probabilityEntry)) {
+            AKLOGE("Cannot set probability entry in runGC. terminalId: %d", it->second);
             return false;
         }
         mSize++;
@@ -90,9 +109,55 @@
     return true;
 }
 
+int ProbabilityDictContent::getEntrySize() const {
+    if (mHasHistoricalInfo) {
+        return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE
+                + Ver4DictConstants::PROBABILITY_SIZE
+                + Ver4DictConstants::TIME_STAMP_FIELD_SIZE
+                + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE
+                + Ver4DictConstants::WORD_COUNT_FIELD_SIZE;
+    } else {
+        return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE
+                + Ver4DictConstants::PROBABILITY_SIZE;
+    }
+}
+
 int ProbabilityDictContent::getEntryPos(const int terminalId) const {
-    return terminalId * (Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE
-            + Ver4DictConstants::PROBABILITY_SIZE);
+    return terminalId * getEntrySize();
+}
+
+bool ProbabilityDictContent::writeEntry(const ProbabilityEntry *const probabilityEntry,
+        const int entryPos) {
+    BufferWithExtendableBuffer *const bufferToWrite = getWritableBuffer();
+    int writingPos = entryPos;
+    if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getFlags(),
+            Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &writingPos)) {
+        AKLOGE("Cannot write flags in probability dict content. pos: %d", writingPos);
+        return false;
+    }
+    if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getProbability(),
+            Ver4DictConstants::PROBABILITY_SIZE, &writingPos)) {
+        AKLOGE("Cannot write probability in probability dict content. pos: %d", writingPos);
+        return false;
+    }
+    if (mHasHistoricalInfo) {
+        if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getTimeStamp(),
+                Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &writingPos)) {
+            AKLOGE("Cannot write timestamp in probability dict content. pos: %d", writingPos);
+            return false;
+        }
+        if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getLevel(),
+                Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &writingPos)) {
+            AKLOGE("Cannot write level in probability dict content. pos: %d", writingPos);
+            return false;
+        }
+        if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getCount(),
+                Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &writingPos)) {
+            AKLOGE("Cannot write count in probability dict content. pos: %d", writingPos);
+            return false;
+        }
+    }
+    return true;
 }
 
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h
index 0971ee0..7e78272 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h
@@ -25,18 +25,23 @@
 
 namespace latinime {
 
+class ProbabilityEntry;
+
 class ProbabilityDictContent : public SingleDictContent {
  public:
-    ProbabilityDictContent(const char *const dictDirPath, const bool isUpdatable)
+    ProbabilityDictContent(const char *const dictDirPath, const bool hasHistoricalInfo,
+            const bool isUpdatable)
             : SingleDictContent(dictDirPath, Ver4DictConstants::FREQ_FILE_EXTENSION, isUpdatable),
-              mSize(getBuffer()->getTailPosition() / (Ver4DictConstants::PROBABILITY_SIZE
-                      + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE)) {}
+              mHasHistoricalInfo(hasHistoricalInfo),
+              mSize(getBuffer()->getTailPosition() / getEntrySize()) {}
 
-    ProbabilityDictContent() : mSize(0) {}
+    ProbabilityDictContent(const bool hasHistoricalInfo)
+            : mHasHistoricalInfo(hasHistoricalInfo), mSize(0) {}
 
-    int getProbability(const int terminalId) const;
+    void getProbabilityEntry(const int terminalId,
+            ProbabilityEntry *const outProbabilityEntry) const;
 
-    bool setProbability(const int terminalId, const int probability);
+    bool setProbabilityEntry(const int terminalId, const ProbabilityEntry *const probabilityEntry);
 
     bool flushToFile(const char *const dictDirPath) const;
 
@@ -46,8 +51,13 @@
  private:
     DISALLOW_COPY_AND_ASSIGN(ProbabilityDictContent);
 
+    int getEntrySize() const;
+
     int getEntryPos(const int terminalId) const;
 
+    bool writeEntry(const ProbabilityEntry *const probabilityEntry, const int entryPos);
+
+    bool mHasHistoricalInfo;
     int mSize;
 };
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
new file mode 100644
index 0000000..95e2e28
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_PROBABILITY_ENTRY_H
+#define LATINIME_PROBABILITY_ENTRY_H
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
+
+namespace latinime {
+
+class ProbabilityEntry {
+ public:
+    ProbabilityEntry()
+            : mFlags(0), mProbability(NOT_A_PROBABILITY),
+              mTimestamp(Ver4DictConstants::NOT_A_TIME_STAMP), mLevel(0), mCount(0) {}
+
+    void setProbability(const int flags, const int probability) {
+        mFlags = flags;
+        mProbability = probability;
+        mTimestamp = Ver4DictConstants::NOT_A_TIME_STAMP;
+        mLevel = 0;
+        mCount = 0;
+    }
+
+    void setProbabilityWithHistricalInfo(const int flags, const int probability,
+            const int timestamp, const int level, const int count) {
+        mFlags = flags;
+        mProbability = probability;
+        mTimestamp = timestamp;
+        mLevel = level;
+        mCount = count;
+    }
+
+    int getFlags() const {
+        return mFlags;
+    }
+
+    int getProbability() const {
+        return mProbability;
+    }
+
+    int getTimeStamp() const {
+        return mTimestamp;
+    }
+
+    int getLevel() const {
+        return mLevel;
+    }
+
+    int getCount() const {
+        return mCount;
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(ProbabilityEntry);
+
+    int mFlags;
+    int mProbability;
+    int mTimestamp;
+    int mLevel;
+    int mCount;
+};
+} // namespace latinime
+#endif /* LATINIME_PROBABILITY_ENTRY_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
index e67bd2e..8fdbbed 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
@@ -126,7 +126,7 @@
                       BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE),
               // TODO: Quit using header size.
               mTerminalPositionLookupTable(dictDirPath, isUpdatable, mHeaderSize),
-              mProbabilityDictContent(dictDirPath, isUpdatable),
+              mProbabilityDictContent(dictDirPath, false /* hasHistoricalInfo */, isUpdatable),
               mBigramDictContent(dictDirPath, isUpdatable),
               mShortcutDictContent(dictDirPath, isUpdatable),
               mIsUpdatable(isUpdatable) {}
@@ -135,7 +135,8 @@
             : mDictBuffer(0), mHeaderSize(0),
               mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
               mExpandableTrieBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
-              mTerminalPositionLookupTable(), mProbabilityDictContent(),
+              mTerminalPositionLookupTable(),
+              mProbabilityDictContent(false /* hasHistoricalInfo */),
               mBigramDictContent(), mShortcutDictContent(), mIsUpdatable(true) {}
 
     const MmappedBuffer::MmappedBufferPtr mDictBuffer;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
index 3632151..457f296 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
@@ -45,6 +45,8 @@
 const int Ver4DictConstants::NOT_A_TERMINAL_ADDRESS = 0;
 const int Ver4DictConstants::TERMINAL_ID_FIELD_SIZE = 4;
 const int Ver4DictConstants::TIME_STAMP_FIELD_SIZE = 4;
+const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 1;
+const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 1;
 
 const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4;
 const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE = 4;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp
index e9fba79..9f7b34d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp
@@ -19,6 +19,7 @@
 #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h"
+#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 
@@ -59,7 +60,9 @@
             terminalIdFieldPos += mBuffer->getOriginalBufferSize();
         }
         terminalId = Ver4PatriciaTrieReadingUtils::getTerminalIdAndAdvancePosition(dictBuf, &pos);
-        probability = mProbabilityDictContent->getProbability(terminalId);
+        ProbabilityEntry probabilityEntry;
+        mProbabilityDictContent->getProbabilityEntry(terminalId, &probabilityEntry);
+        probability = probabilityEntry.getProbability();
     }
     int childrenPosFieldPos = pos;
     if (usesAdditionalBuffer) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
index 6a41827..145eeb0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
@@ -19,6 +19,7 @@
 #include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h"
 #include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h"
 #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h"
 #include "suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_writing_utils.h"
@@ -119,8 +120,12 @@
     }
     const int probabilityToWrite = getUpdatedProbability(toBeUpdatedPtNodeParams->getProbability(),
             newProbability);
-    return mBuffers->getUpdatableProbabilityDictContent()->setProbability(
-            toBeUpdatedPtNodeParams->getTerminalId(), probabilityToWrite);
+    ProbabilityEntry probabilityEntry;
+    mBuffers->getProbabilityDictContent()->getProbabilityEntry(
+            toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry);
+    probabilityEntry.setProbability(probabilityEntry.getFlags(), probabilityToWrite);
+    return mBuffers->getUpdatableProbabilityDictContent()->setProbabilityEntry(
+            toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry);
 }
 
 bool Ver4PatriciaTrieNodeWriter::updateChildrenPosition(
@@ -153,8 +158,10 @@
     // Write probability.
     const int probabilityToWrite = getUpdatedProbability(NOT_A_PROBABILITY,
             ptNodeParams->getProbability());
-    return mBuffers->getUpdatableProbabilityDictContent()->setProbability(terminalId,
-            probabilityToWrite);
+    ProbabilityEntry probabilityEntry;
+    probabilityEntry.setProbability(0 /* flags */, probabilityToWrite);
+    return mBuffers->getUpdatableProbabilityDictContent()->setProbabilityEntry(terminalId,
+            &probabilityEntry);
 }
 
 bool Ver4PatriciaTrieNodeWriter::addNewBigramEntry(
@@ -258,13 +265,6 @@
                 Ver4DictConstants::TERMINAL_ID_FIELD_SIZE, ptNodeWritingPos)) {
             return false;
         }
-        // Write probability.
-        if (ptNodeParams->getProbability() != NOT_A_PROBABILITY) {
-            if (!mBuffers->getUpdatableProbabilityDictContent()->setProbability(
-                    terminalId, ptNodeParams->getProbability())) {
-                return false;
-            }
-        }
         if (outTerminalId) {
             *outTerminalId = terminalId;
         }