Merge "Import translations. DO NOT MERGE" into jb-mr1-dev
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 3e83fc0..478dc0f 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -92,11 +92,6 @@
             android:summary="@string/gesture_input_summary"
             android:persistent="true"
             android:defaultValue="true" />
-        <CheckBoxPreference
-            android:key="usability_study_mode"
-            android:title="@string/prefs_usability_study_mode"
-            android:persistent="true"
-            android:defaultValue="false" />
         <PreferenceScreen
             android:key="pref_advanced_settings"
             android:title="@string/advanced_settings"
@@ -145,5 +140,10 @@
                 android:persistent="true"
                 android:defaultValue="false" />
         </PreferenceScreen>
+        <PreferenceScreen
+            android:key="debug_settings"
+            android:title="Debug settings"
+            android:persistent="true"
+            android:defaultValue="false" />
     </PreferenceCategory>
 </PreferenceScreen>
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index b926ed0..605a02f 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -48,4 +48,10 @@
             android:persistent="true"
             android:defaultValue="false"
             />
+
+    <CheckBoxPreference
+            android:key="usability_study_mode"
+            android:title="@string/prefs_usability_study_mode"
+            android:persistent="true"
+            android:defaultValue="false" />
 </PreferenceScreen>
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 03d610a..d4902ec 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -688,7 +688,7 @@
         mIsDetectingGesture = true;
         final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
         mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown,
-                false /* isHistorical */);
+                true /* isMajorEvent */);
     }
 
     private void onDownEventInternal(final int x, final int y, final long eventTime) {
@@ -724,10 +724,10 @@
     }
 
     private void onGestureMoveEvent(final int x, final int y, final long eventTime,
-            final boolean isHistorical, final Key key) {
+            final boolean isMajorEvent, final Key key) {
         final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
         if (mIsDetectingGesture) {
-            mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isHistorical);
+            mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent);
             mayStartBatchInput();
             if (sInGesture && key != null) {
                 updateBatchInput(eventTime);
@@ -752,7 +752,7 @@
                 final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
                 final long historicalTime = me.getHistoricalEventTime(h);
                 onGestureMoveEvent(historicalX, historicalY, historicalTime,
-                        true /* isHistorical */, null);
+                        false /* isMajorEvent */, null);
             }
         }
 
@@ -767,7 +767,7 @@
 
         if (sShouldHandleGesture) {
             // Register move event on gesture tracker.
-            onGestureMoveEvent(x, y, eventTime, false /* isHistorical */, key);
+            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key);
             if (sInGesture) {
                 mIgnoreModifierKey = true;
                 mTimerProxy.cancelLongPressTimer();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
index f0be0ee..73413f6 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -14,34 +14,45 @@
 
 package com.android.inputmethod.keyboard.internal;
 
+import android.util.Log;
+
 import com.android.inputmethod.latin.InputPointers;
 import com.android.inputmethod.latin.ResizableIntArray;
 
 public class GestureStroke {
+    private static final String TAG = GestureStroke.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
     public static final int DEFAULT_CAPACITY = 128;
 
     private final int mPointerId;
     private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
     private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
     private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
-    private float mLength;
     private int mIncrementalRecognitionSize;
     private int mLastIncrementalBatchSize;
-    private long mLastPointTime;
-    private int mLastPointX;
-    private int mLastPointY;
+    private long mLastMajorEventTime;
+    private int mLastMajorEventX;
+    private int mLastMajorEventY;
 
-    private int mMinGestureLength; // pixel
-    private int mMinGestureSampleLength; // pixel
-    private int mGestureRecognitionThreshold; // pixel / sec
+    private int mKeyWidth;
+    private int mStartGestureLengthThreshold; // pixel
+    private int mMinGestureSamplingLength; // pixel
+    private int mGestureRecognitionSpeedThreshold; // pixel / sec
+    private int mDetectFastMoveSpeedThreshold; // pixel /sec
+    private int mDetectFastMoveTime;
+    private int mDetectFastMoveX;
+    private int mDetectFastMoveY;
 
     // TODO: Move some of these to resource.
-    private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 0.75f;
-    private static final int MIN_GESTURE_START_DURATION = 100; // msec
+    private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.75f;
+    private static final int START_GESTURE_DURATION_THRESHOLD = 70; // msec
     private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
     private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f;
     private static final float GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH =
             5.5f; // keyWidth / sec
+    private static final float DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH =
+            5.0f; // keyWidth / sec
     private static final int MSEC_PER_SEC = 1000;
 
     public static final boolean hasRecognitionTimePast(
@@ -54,64 +65,120 @@
     }
 
     public void setKeyboardGeometry(final int keyWidth) {
+        mKeyWidth = keyWidth;
         // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
-        mMinGestureLength = (int)(keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH);
-        mMinGestureSampleLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
-        mGestureRecognitionThreshold =
+        mStartGestureLengthThreshold =
+                (int)(keyWidth * START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH);
+        mMinGestureSamplingLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
+        mGestureRecognitionSpeedThreshold =
                 (int)(keyWidth * GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
+        mDetectFastMoveSpeedThreshold =
+                (int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
+        if (DEBUG) {
+            Log.d(TAG, "setKeyboardGeometry: keyWidth=" + keyWidth);
+        }
     }
 
     public boolean isStartOfAGesture() {
+        if (mDetectFastMoveTime == 0) {
+            return false;
+        }
         final int size = mEventTimes.getLength();
-        final int downDuration = (size > 0) ? mEventTimes.get(size - 1) : 0;
-        return downDuration > MIN_GESTURE_START_DURATION && mLength > mMinGestureLength;
+        if (size <= 0) {
+            return false;
+        }
+        final int lastIndex = size - 1;
+        final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime;
+        final int deltaLength = getDistance(
+                mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
+                mDetectFastMoveX, mDetectFastMoveY);
+        final boolean isStartOfAGesture = deltaTime > START_GESTURE_DURATION_THRESHOLD
+                && deltaLength > mStartGestureLengthThreshold;
+        if (DEBUG) {
+            Log.d(TAG, "isStartOfAGesture: dT=" + deltaTime + " dL=" + deltaLength
+                    + " points=" + size + (isStartOfAGesture ? " Detect start of a gesture" : ""));
+        }
+        return isStartOfAGesture;
     }
 
     public void reset() {
-        mLength = 0;
         mIncrementalRecognitionSize = 0;
         mLastIncrementalBatchSize = 0;
-        mLastPointTime = 0;
         mEventTimes.setLength(0);
         mXCoordinates.setLength(0);
         mYCoordinates.setLength(0);
+        mLastMajorEventTime = 0;
+        mDetectFastMoveTime = 0;
     }
 
-    public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
-        final boolean needsSampling;
+    private void appendPoint(final int x, final int y, final int time) {
+        mEventTimes.add(time);
+        mXCoordinates.add(x);
+        mYCoordinates.add(y);
+    }
+
+    private void updateMajorEvent(final int x, final int y, final int time) {
+        mLastMajorEventTime = time;
+        mLastMajorEventX = x;
+        mLastMajorEventY = y;
+    }
+
+    private int detectFastMove(final int x, final int y, final int time) {
         final int size = mEventTimes.getLength();
-        if (size == 0) {
-            needsSampling = true;
+        final int lastIndex = size - 1;
+        final int lastX = mXCoordinates.get(lastIndex);
+        final int lastY = mYCoordinates.get(lastIndex);
+        final int dist = getDistance(lastX, lastY, x, y);
+        final int msecs = time - mEventTimes.get(lastIndex);
+        if (msecs > 0) {
+            final int pixels = getDistance(lastX, lastY, x, y);
+            final int pixelsPerSec = pixels * MSEC_PER_SEC;
+            if (DEBUG) {
+                final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
+                Log.d(TAG, String.format("Speed=%.3f keyWidth/sec", speed));
+            }
+            // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
+            if (mDetectFastMoveTime == 0 && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
+                if (DEBUG) {
+                    Log.d(TAG, "Detect fast move: T=" + time + " points = " + size);
+                }
+                mDetectFastMoveTime = time;
+                mDetectFastMoveX = x;
+                mDetectFastMoveY = y;
+            }
+        }
+        return dist;
+    }
+
+    public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) {
+        final int size = mEventTimes.getLength();
+        if (size <= 0) {
+            // Down event
+            appendPoint(x, y, time);
+            updateMajorEvent(x, y, time);
         } else {
-            final int lastIndex = size - 1;
-            final int lastX = mXCoordinates.get(lastIndex);
-            final int lastY = mYCoordinates.get(lastIndex);
-            final float dist = getDistance(lastX, lastY, x, y);
-            needsSampling = dist > mMinGestureSampleLength;
-            mLength += dist;
+            final int dist = detectFastMove(x, y, time);
+            if (dist > mMinGestureSamplingLength) {
+                appendPoint(x, y, time);
+            }
         }
-        if (needsSampling) {
-            mEventTimes.add(time);
-            mXCoordinates.add(x);
-            mYCoordinates.add(y);
-        }
-        if (!isHistorical) {
+        if (isMajorEvent) {
             updateIncrementalRecognitionSize(x, y, time);
+            updateMajorEvent(x, y, time);
         }
     }
 
     private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {
-        final int msecs = (int)(time - mLastPointTime);
-        if (msecs > 0) {
-            final int pixels = (int)getDistance(mLastPointX, mLastPointY, x, y);
-            // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC)
-            if (pixels * MSEC_PER_SEC < mGestureRecognitionThreshold * msecs) {
-                mIncrementalRecognitionSize = mEventTimes.getLength();
-            }
+        final int msecs = (int)(time - mLastMajorEventTime);
+        if (msecs <= 0) {
+            return;
         }
-        mLastPointTime = time;
-        mLastPointX = x;
-        mLastPointY = y;
+        final int pixels = getDistance(mLastMajorEventX, mLastMajorEventY, x, y);
+        final int pixelsPerSec = pixels * MSEC_PER_SEC;
+        // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC)
+        if (pixelsPerSec < mGestureRecognitionSpeedThreshold * msecs) {
+            mIncrementalRecognitionSize = mEventTimes.getLength();
+        }
     }
 
     public void appendAllBatchPoints(final InputPointers out) {
@@ -132,11 +199,11 @@
         mLastIncrementalBatchSize = size;
     }
 
-    private static float getDistance(final int x1, final int y1, final int x2, final int y2) {
-        final float dx = x1 - x2;
-        final float dy = y1 - y2;
+    private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
+        final int dx = x1 - x2;
+        final int dy = y1 - y2;
         // Note that, in recent versions of Android, FloatMath is actually slower than
         // java.lang.Math due to the way the JIT optimizes java.lang.Math.
-        return (float)Math.sqrt(dx * dx + dy * dy);
+        return (int)Math.sqrt(dx * dx + dy * dy);
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
index ce39140..3487b50 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
@@ -65,21 +65,18 @@
     private boolean needsSampling(final int x, final int y) {
         final int dx = x - mLastX;
         final int dy = y - mLastY;
-        final boolean needsSampling = (dx * dx + dy * dy >= mMinPreviewSampleLengthSquare);
-        if (needsSampling) {
-            mLastX = x;
-            mLastY = y;
-        }
-        return needsSampling;
+        return dx * dx + dy * dy >= mMinPreviewSampleLengthSquare;
     }
 
     @Override
-    public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
-        super.addPoint(x, y, time, isHistorical);
-        if (mPreviewEventTimes.getLength() == 0 || isHistorical || needsSampling(x, y)) {
+    public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) {
+        super.addPoint(x, y, time, isMajorEvent);
+        if (isMajorEvent || needsSampling(x, y)) {
             mPreviewEventTimes.add(time);
             mPreviewXCoordinates.add(x);
             mPreviewYCoordinates.add(y);
+            mLastX = x;
+            mLastY = y;
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index c3ae81f..9244f16 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -45,7 +45,7 @@
     public static final int MAX_WORDS = 18;
     public static final int MAX_SPACES = 16;
 
-    private static final String TAG = "BinaryDictionary";
+    private static final String TAG = BinaryDictionary.class.getSimpleName();
     private static final int MAX_PREDICTIONS = 60;
     private static final int MAX_RESULTS = Math.max(MAX_PREDICTIONS, MAX_WORDS);
 
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index af76498..1ea14da 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -23,10 +23,12 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.preference.CheckBoxPreference;
+import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import android.util.Log;
 
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.research.ResearchLogger;
 
 public class DebugSettings extends PreferenceFragment
         implements SharedPreferences.OnSharedPreferenceChangeListener {
@@ -34,6 +36,7 @@
     private static final String TAG = DebugSettings.class.getSimpleName();
     private static final String DEBUG_MODE_KEY = "debug_mode";
     public static final String FORCE_NON_DISTINCT_MULTITOUCH_KEY = "force_non_distinct_multitouch";
+    public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
 
     private boolean mServiceNeedsRestart = false;
     private CheckBoxPreference mDebugMode;
@@ -45,6 +48,14 @@
         SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
         prefs.registerOnSharedPreferenceChangeListener(this);
 
+        final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE);
+        if (usabilityStudyPref instanceof CheckBoxPreference) {
+            final CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
+            checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE,
+                    ResearchLogger.DEFAULT_USABILITY_STUDY_MODE));
+            checkbox.setSummary(R.string.settings_warning_researcher_mode);
+        }
+
         mServiceNeedsRestart = false;
         mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY);
         updateDebugMode();
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 9479a88..b984ec3 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -39,12 +39,10 @@
 import android.widget.TextView;
 
 import com.android.inputmethod.latin.define.ProductionFlag;
-import com.android.inputmethod.research.ResearchLogger;
 import com.android.inputmethodcommon.InputMethodSettingsFragment;
 
 public final class Settings extends InputMethodSettingsFragment
         implements SharedPreferences.OnSharedPreferenceChangeListener {
-    public static final boolean ENABLE_INTERNAL_SETTINGS = ProductionFlag.IS_INTERNAL;
 
     // In the same order as xml/prefs.xml
     public static final String PREF_GENERAL_SETTINGS = "general_settings";
@@ -58,7 +56,6 @@
     public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
     public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
     public static final String PREF_MISC_SETTINGS = "misc_settings";
-    public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
     public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME =
             "last_user_dictionary_write_time";
     public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
@@ -133,14 +130,6 @@
         mAutoCorrectionThresholdPreference =
                 (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
         mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
-        mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
-        if (mDebugSettingsPreference != null) {
-            final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
-            debugSettingsIntent.setClassName(
-                    context.getPackageName(), DebugSettings.class.getName());
-            mDebugSettingsPreference.setIntent(debugSettingsIntent);
-        }
-
         ensureConsistencyOfAutoCorrectionSettings();
 
         final PreferenceGroup generalSettings =
@@ -150,6 +139,18 @@
         final PreferenceGroup miscSettings =
                 (PreferenceGroup) findPreference(PREF_MISC_SETTINGS);
 
+        mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
+        if (mDebugSettingsPreference != null) {
+            if (ProductionFlag.IS_INTERNAL) {
+                final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
+                debugSettingsIntent.setClassName(
+                        context.getPackageName(), DebugSettingsActivity.class.getName());
+                mDebugSettingsPreference.setIntent(debugSettingsIntent);
+            } else {
+                miscSettings.removePreference(mDebugSettingsPreference);
+            }
+        }
+
         final boolean showVoiceKeyOption = res.getBoolean(
                 R.bool.config_enable_show_voice_key_option);
         if (!showVoiceKeyOption) {
@@ -218,24 +219,6 @@
             setPreferenceEnabled(gestureFloatingPreviewText, gestureInputEnabledByUser);
         }
 
-        final boolean showUsabilityStudyModeOption =
-                res.getBoolean(R.bool.config_enable_usability_study_mode_option)
-                        || ProductionFlag.IS_EXPERIMENTAL || ENABLE_INTERNAL_SETTINGS;
-        final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE);
-        if (!showUsabilityStudyModeOption) {
-            if (usabilityStudyPref != null) {
-                miscSettings.removePreference(usabilityStudyPref);
-            }
-        }
-        if (ProductionFlag.IS_EXPERIMENTAL) {
-            if (usabilityStudyPref instanceof CheckBoxPreference) {
-                CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
-                checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE,
-                        ResearchLogger.DEFAULT_USABILITY_STUDY_MODE));
-                checkbox.setSummary(R.string.settings_warning_researcher_mode);
-            }
-        }
-
         mKeypressVibrationDurationSettingsPref =
                 (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS);
         if (mKeypressVibrationDurationSettingsPref != null) {
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 9d8379a..5b8f1cf 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -410,7 +410,7 @@
     // Likewise
     public static boolean getUsabilityStudyMode(final SharedPreferences prefs) {
         // TODO: use mUsabilityStudyMode instead of reading it again here
-        return prefs.getBoolean(Settings.PREF_USABILITY_STUDY_MODE, true);
+        return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true);
     }
 
     public static long getLastUserHistoryWriteTime(final SharedPreferences prefs,
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index 683ee4f..e03af64 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -42,7 +42,7 @@
  * cancellation or manual picks. This allows the keyboard to adapt to the typist over time.
  */
 public class UserHistoryDictionary extends ExpandableDictionary {
-    private static final String TAG = "UserHistoryDictionary";
+    private static final String TAG = UserHistoryDictionary.class.getSimpleName();
     public static final boolean DBG_SAVE_RESTORE = false;
     public static final boolean DBG_STRESS_TEST = false;
     public static final boolean DBG_ALWAYS_WRITE = false;
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 4060710..3975329 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -201,4 +201,26 @@
         }
         return FormatSpec.NOT_VALID_WORD;
     }
+
+    /**
+     * Delete the word from the binary file.
+     *
+     * @param buffer the buffer to write.
+     * @param word the word we delete
+     * @throws IOException
+     * @throws UnsupportedFormatException
+     */
+    public static void deleteWord(final FusionDictionaryBufferInterface buffer,
+            final String word) throws IOException, UnsupportedFormatException {
+        buffer.position(0);
+        final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+        final int wordPosition = getTerminalPosition(buffer, word);
+        if (wordPosition == FormatSpec.NOT_VALID_WORD) return;
+
+        buffer.position(wordPosition);
+        final int flags = buffer.readUnsignedByte();
+        final int newFlags = flags ^ FormatSpec.FLAG_IS_TERMINAL;
+        buffer.position(wordPosition);
+        buffer.put((byte)newFlags);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index 1d3e94b..7b8dc5c 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -392,7 +392,7 @@
     /**
      * Helper method to check whether the CharGroup has a parent address.
      */
-    private static boolean hasParentAddress(final FormatOptions options) {
+    public static boolean hasParentAddress(final FormatOptions options) {
         return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_PARENT_ADDRESS
                 && options.mHasParentAddress;
     }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
index 24776d5..539021f 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
@@ -25,6 +25,7 @@
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 
 import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -517,7 +518,7 @@
     public void testGetTerminalPosition() {
         File file = null;
         try {
-            file = File.createTempFile("runReadUnigrams", ".dict");
+            file = File.createTempFile("testGetTerminalPosition", ".dict");
         } catch (IOException e) {
             // do nothing
         }
@@ -564,4 +565,38 @@
             runGetTerminalPosition(buffer, word, i, false);
         }
     }
+
+    public void testDeleteWord() {
+        File file = null;
+        try {
+            file = File.createTempFile("testGetTerminalPosition", ".dict");
+        } catch (IOException e) {
+            // do nothing
+        }
+        assertNotNull(file);
+
+        final FusionDictionary dict = new FusionDictionary(new Node(),
+                new FusionDictionary.DictionaryOptions(
+                        new HashMap<String, String>(), false, false));
+        addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
+        timeWritingDictToFile(file, dict, VERSION3_WITH_LINKEDLIST_NODE);
+
+        final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY);
+
+        try {
+            MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
+                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0)));
+            BinaryDictIOUtils.deleteWord(buffer, sWords.get(0));
+            assertEquals(FormatSpec.NOT_VALID_WORD,
+                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0)));
+
+            MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
+                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5)));
+            BinaryDictIOUtils.deleteWord(buffer, sWords.get(5));
+            assertEquals(FormatSpec.NOT_VALID_WORD,
+                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5)));
+        } catch (IOException e) {
+        } catch (UnsupportedFormatException e) {
+        }
+    }
 }