Merge "Revert "ResearchLogger to track simple statistics"" into jb-mr1-dev
diff --git a/java/res/values-en/whitelist.xml b/java/res/values-en/whitelist.xml
deleted file mode 100644
index 2620179..0000000
--- a/java/res/values-en/whitelist.xml
+++ /dev/null
@@ -1,411 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!--
-        An entry of the whitelist word should be:
-        1. (int)frequency
-        2. (String)before
-        3. (String)after
-     -->
-    <string-array name="wordlist_whitelist" translatable="false">
-
-        <item>255</item>
-        <item>ill</item>
-        <item>I\'ll</item>
-
-        <!-- TODO: Trim down more entries by removing ones that get auto-corrected by the
-             Android keyboard's own typing error correction algorithms. -->
-
-        <item>255</item>
-        <item>acomodate</item>
-        <item>accommodate</item>
-
-        <item>255</item>
-        <item>aint</item>
-        <item>ain\'t</item>
-
-        <item>255</item>
-        <item>alot</item>
-        <item>a lot</item>
-
-        <item>255</item>
-        <item>andteh</item>
-        <item>and the</item>
-
-        <item>255</item>
-        <item>arent</item>
-        <item>aren\'t</item>
-
-        <item>255</item>
-        <item>bot</item>
-        <item>not</item>
-
-        <item>255</item>
-        <item>bern</item>
-        <item>been</item>
-
-        <item>255</item>
-        <item>bot</item>
-        <item>not</item>
-
-        <item>255</item>
-        <item>bur</item>
-        <item>but</item>
-
-        <item>255</item>
-        <item>cam</item>
-        <item>can</item>
-
-        <item>255</item>
-        <item>cant</item>
-        <item>can\'t</item>
-
-        <item>255</item>
-        <item>dame</item>
-        <item>same</item>
-
-        <item>255</item>
-        <item>didint</item>
-        <item>didn\'t</item>
-
-        <item>255</item>
-        <item>dormer</item>
-        <item>former</item>
-
-        <item>255</item>
-        <item>dud</item>
-        <item>did</item>
-
-        <item>255</item>
-        <item>fay</item>
-        <item>day</item>
-
-        <item>255</item>
-        <item>fife</item>
-        <item>five</item>
-
-        <item>255</item>
-        <item>foo</item>
-        <item>for</item>
-
-        <item>255</item>
-        <item>fora</item>
-        <item>for a</item>
-
-        <item>255</item>
-        <item>galled</item>
-        <item>called</item>
-
-        <item>255</item>
-        <item>goo</item>
-        <item>too</item>
-
-        <item>255</item>
-        <item>hed</item>
-        <item>he\'d</item>
-
-        <item>255</item>
-        <item>hel</item>
-        <item>he\'ll</item>
-
-        <item>255</item>
-        <item>heres</item>
-        <item>here\'s</item>
-
-        <item>255</item>
-        <item>hew</item>
-        <item>new</item>
-
-        <item>255</item>
-        <item>hoe</item>
-        <item>how</item>
-
-        <item>255</item>
-        <item>hoes</item>
-        <item>how\'s</item>
-
-        <item>255</item>
-        <item>howd</item>
-        <item>how\'d</item>
-
-        <item>255</item>
-        <item>howll</item>
-        <item>how\'ll</item>
-
-        <item>255</item>
-        <item>hows</item>
-        <item>how\'s</item>
-
-        <item>255</item>
-        <item>howve</item>
-        <item>how\'ve</item>
-
-        <item>255</item>
-        <item>hum</item>
-        <item>him</item>
-
-        <item>255</item>
-        <item>i</item>
-        <item>I</item>
-
-        <item>255</item>
-        <item>ifs</item>
-        <item>its</item>
-
-        <item>255</item>
-        <item>il</item>
-        <item>I\'ll</item>
-
-        <item>255</item>
-        <item>im</item>
-        <item>I\'m</item>
-
-        <item>255</item>
-        <item>inteh</item>
-        <item>in the</item>
-
-        <item>255</item>
-        <item>itd</item>
-        <item>it\'d</item>
-
-        <item>255</item>
-        <item>itsa</item>
-        <item>it\'s a</item>
-
-        <item>255</item>
-        <item>lets</item>
-        <item>let\'s</item>
-
-        <item>255</item>
-        <item>maam</item>
-        <item>ma\'am</item>
-
-        <item>255</item>
-        <item>manu</item>
-        <item>many</item>
-
-        <item>255</item>
-        <item>mare</item>
-        <item>made</item>
-
-        <item>255</item>
-        <item>mew</item>
-        <item>new</item>
-
-        <item>255</item>
-        <item>mire</item>
-        <item>more</item>
-
-        <item>255</item>
-        <item>moat</item>
-        <item>most</item>
-
-        <item>255</item>
-        <item>mot</item>
-        <item>not</item>
-
-        <item>255</item>
-        <item>mote</item>
-        <item>note</item>
-
-        <item>255</item>
-        <item>motes</item>
-        <item>notes</item>
-
-        <item>255</item>
-        <item>mow</item>
-        <item>now</item>
-
-        <item>255</item>
-        <item>namer</item>
-        <item>named</item>
-
-        <item>255</item>
-        <item>nave</item>
-        <item>have</item>
-
-        <item>255</item>
-        <item>nee</item>
-        <item>new</item>
-
-        <item>255</item>
-        <item>nigh</item>
-        <item>high</item>
-
-        <item>255</item>
-        <item>nit</item>
-        <item>not</item>
-
-        <item>255</item>
-        <item>oft</item>
-        <item>off</item>
-
-        <item>255</item>
-        <item>os</item>
-        <item>is</item>
-
-        <item>255</item>
-        <item>pater</item>
-        <item>later</item>
-
-        <item>255</item>
-        <item>rook</item>
-        <item>took</item>
-
-        <item>255</item>
-        <item>shel</item>
-        <item>she\'ll</item>
-
-        <item>255</item>
-        <item>shouldent</item>
-        <item>shouldn\'t</item>
-
-        <item>255</item>
-        <item>sill</item>
-        <item>will</item>
-
-        <item>255</item>
-        <item>sown</item>
-        <item>down</item>
-
-        <item>255</item>
-        <item>thatd</item>
-        <item>that\'d</item>
-
-        <item>255</item>
-        <item>tine</item>
-        <item>time</item>
-
-        <item>255</item>
-        <item>thong</item>
-        <item>thing</item>
-
-        <item>255</item>
-        <item>tome</item>
-        <item>time</item>
-
-        <!-- through additional proximity, 'uf' becomes 'of'. 'o' is not next to 'u' so anyone
-             typing 'uf' probably meant 'if', but 'of' is much more common and should be left
-             higher than 'if', hence the need for this entry. -->
-        <item>255</item>
-        <item>uf</item>
-        <item>if</item>
-
-        <!-- 'un' becomes 'UN' because of perfect match ; even if we remove 'UN', then 'un'
-             will become 'on' for the same reason as above. So list this here. -->
-        <item>255</item>
-        <item>un</item>
-        <item>in</item>
-
-        <!-- does it really make any sense to have the following here? -->
-        <item>255</item>
-        <item>UnitedStates</item>
-        <item>United States</item>
-
-        <item>255</item>
-        <item>unitedstates</item>
-        <item>United States</item>
-
-        <item>255</item>
-        <item>visavis</item>
-        <item>vis-a-vis</item>
-
-        <item>255</item>
-        <item>wierd</item>
-        <item>weird</item>
-
-        <item>255</item>
-        <item>wel</item>
-        <item>we\'ll</item>
-
-        <item>255</item>
-        <item>wer</item>
-        <item>we\'re</item>
-
-        <item>255</item>
-        <item>whatd</item>
-        <item>what\'d</item>
-
-        <item>255</item>
-        <item>whatm</item>
-        <item>what\'m</item>
-
-        <item>255</item>
-        <item>whatre</item>
-        <item>what\'re</item>
-
-        <item>255</item>
-        <item>whats</item>
-        <item>what\'s</item>
-
-        <item>255</item>
-        <item>whens</item>
-        <item>when\'s</item>
-
-        <item>255</item>
-        <item>whered</item>
-        <item>where\'d</item>
-
-        <item>255</item>
-        <item>wherell</item>
-        <item>where\'ll</item>
-
-        <item>255</item>
-        <item>wheres</item>
-        <item>where\'s</item>
-
-        <item>255</item>
-        <item>wholl</item>
-        <item>who\'ll</item>
-
-        <item>255</item>
-        <item>whove</item>
-        <item>who\'ve</item>
-
-        <item>255</item>
-        <item>whyd</item>
-        <item>why\'d</item>
-
-        <item>255</item>
-        <item>whyll</item>
-        <item>why\'ll</item>
-
-        <item>255</item>
-        <item>whys</item>
-        <item>why\'s</item>
-
-        <item>255</item>
-        <item>whyve</item>
-        <item>why\'ve</item>
-
-        <item>255</item>
-        <item>wont</item>
-        <item>won\'t</item>
-
-        <item>255</item>
-        <item>yall</item>
-        <item>y\'all</item>
-
-        <item>255</item>
-        <item>youd</item>
-        <item>you\'d</item>
-
-    </string-array>
-</resources>
diff --git a/java/res/values/whitelist.xml b/java/res/values/whitelist.xml
deleted file mode 100644
index d4ecbfa..0000000
--- a/java/res/values/whitelist.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!--
-        An entry of the whitelist word should be:
-        1. (int)frequency
-        2. (String)before
-        3. (String)after
-     -->
-    <string-array name="wordlist_whitelist">
-    </string-array>
-</resources>
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 9198500..489cc37 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -283,9 +283,10 @@
             public void load(String[] data) {
                 final int dataLength = data.length;
                 if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
-                    if (LatinImeLogger.sDBG)
+                    if (LatinImeLogger.sDBG) {
                         throw new RuntimeException(
                                 "the size of touch position correction data is invalid");
+                    }
                     return;
                 }
 
@@ -324,7 +325,7 @@
 
             public boolean isValid() {
                 return mEnabled && mXs != null && mYs != null && mRadii != null
-                    && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
+                        && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
             }
         }
 
@@ -870,10 +871,12 @@
             final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard);
             try {
-                if (a.hasValue(R.styleable.Keyboard_horizontalGap))
+                if (a.hasValue(R.styleable.Keyboard_horizontalGap)) {
                     throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
-                if (a.hasValue(R.styleable.Keyboard_verticalGap))
+                }
+                if (a.hasValue(R.styleable.Keyboard_verticalGap)) {
                     throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
+                }
                 return new Row(mResources, mParams, parser, mCurrentY);
             } finally {
                 a.recycle();
@@ -921,7 +924,9 @@
                 throws XmlPullParserException, IOException {
             if (skip) {
                 XmlParseUtils.checkEndTag(TAG_KEY, parser);
-                if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
+                if (DEBUG) {
+                    startEndTag("<%s /> skipped", TAG_KEY);
+                }
             } else {
                 final Key key = new Key(mResources, mParams, row, parser);
                 if (DEBUG) {
@@ -1099,9 +1104,9 @@
 
         private boolean parseCaseCondition(XmlPullParser parser) {
             final KeyboardId id = mParams.mId;
-            if (id == null)
+            if (id == null) {
                 return true;
-
+            }
             final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard_Case);
             try {
@@ -1205,9 +1210,9 @@
             // If <case> does not have "index" attribute, that means this <case> is wild-card for
             // the attribute.
             final TypedValue v = a.peekValue(index);
-            if (v == null)
+            if (v == null) {
                 return true;
-
+            }
             if (isIntegerValue(v)) {
                 return intValue == a.getInt(index, 0);
             } else if (isStringValue(v)) {
@@ -1218,8 +1223,9 @@
 
         private static boolean stringArrayContains(String[] array, String value) {
             for (final String elem : array) {
-                if (elem.equals(value))
+                if (elem.equals(value)) {
                     return true;
+                }
             }
             return false;
         }
@@ -1242,16 +1248,18 @@
             TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard_Key);
             try {
-                if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
+                if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) {
                     throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
                             + "/> needs styleName attribute", parser);
+                }
                 if (DEBUG) {
                     startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
-                        keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
-                        skip ? " skipped" : "");
+                            keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
+                            skip ? " skipped" : "");
                 }
-                if (!skip)
+                if (!skip) {
                     mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
+                }
             } finally {
                 keyStyleAttr.recycle();
                 keyAttrs.recycle();
@@ -1272,8 +1280,9 @@
         }
 
         private void endRow(Row row) {
-            if (mCurrentRow == null)
+            if (mCurrentRow == null) {
                 throw new InflateException("orphan end row tag");
+            }
             if (mRightEdgeKey != null) {
                 mRightEdgeKey.markAsRightEdge(mParams);
                 mRightEdgeKey = null;
@@ -1309,8 +1318,9 @@
         public static float getDimensionOrFraction(TypedArray a, int index, int base,
                 float defValue) {
             final TypedValue value = a.peekValue(index);
-            if (value == null)
+            if (value == null) {
                 return defValue;
+            }
             if (isFractionValue(value)) {
                 return a.getFraction(index, base, base, defValue);
             } else if (isDimensionValue(value)) {
@@ -1321,8 +1331,9 @@
 
         public static int getEnumValue(TypedArray a, int index, int defValue) {
             final TypedValue value = a.peekValue(index);
-            if (value == null)
+            if (value == null) {
                 return defValue;
+            }
             if (isIntegerValue(value)) {
                 return a.getInt(index, defValue);
             }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index fcf97b9..ccbb081 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -362,9 +362,9 @@
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
-
         mKeyDrawParams = new KeyDrawParams(a);
         mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams);
+        mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
         mKeyPreviewLayoutId = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
         if (mKeyPreviewLayoutId == 0) {
             mShowKeyPreviewPopup = false;
@@ -373,11 +373,9 @@
                 R.styleable.KeyboardView_verticalCorrection, 0);
         mMoreKeysLayout = a.getResourceId(R.styleable.KeyboardView_moreKeysLayout, 0);
         mBackgroundDimAlpha = a.getInt(R.styleable.KeyboardView_backgroundDimAlpha, 0);
-        mPreviewPlacerView = new PreviewPlacerView(context, a);
         a.recycle();
 
-        mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
-
+        mPreviewPlacerView = new PreviewPlacerView(context, attrs);
         mPaint.setAntiAlias(true);
     }
 
@@ -462,12 +460,7 @@
         if (bufferNeedsUpdates || mOffscreenBuffer == null) {
             if (maybeAllocateOffscreenBuffer()) {
                 mInvalidateAllKeys = true;
-                // TODO: Stop using the offscreen canvas even when in software rendering
-                if (mOffscreenCanvas != null) {
-                    mOffscreenCanvas.setBitmap(mOffscreenBuffer);
-                } else {
-                    mOffscreenCanvas = new Canvas(mOffscreenBuffer);
-                }
+                maybeCreateOffscreenCanvas();
             }
             onDrawKeyboard(mOffscreenCanvas);
         }
@@ -496,6 +489,15 @@
         }
     }
 
+    private void maybeCreateOffscreenCanvas() {
+        // TODO: Stop using the offscreen canvas even when in software rendering
+        if (mOffscreenCanvas != null) {
+            mOffscreenCanvas.setBitmap(mOffscreenBuffer);
+        } else {
+            mOffscreenCanvas = new Canvas(mOffscreenBuffer);
+        }
+    }
+
     private void onDrawKeyboard(final Canvas canvas) {
         if (mKeyboard == null) return;
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
index d0fecf0..59a92d6 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -23,6 +23,7 @@
 import android.graphics.Paint.Align;
 import android.os.Message;
 import android.text.TextUtils;
+import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.widget.RelativeLayout;
 
@@ -49,6 +50,9 @@
     private final SparseArray<PointerTracker> mPointers = new SparseArray<PointerTracker>();
 
     private String mGestureFloatingPreviewText;
+    private int mLastPointerX;
+    private int mLastPointerY;
+
     private boolean mDrawsGesturePreviewTrail;
     private boolean mDrawsGestureFloatingPreviewText;
 
@@ -89,10 +93,16 @@
         }
     }
 
-    public PreviewPlacerView(Context context, TypedArray keyboardViewAttr) {
+    public PreviewPlacerView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.keyboardViewStyle);
+    }
+
+    public PreviewPlacerView(Context context, AttributeSet attrs, int defStyle) {
         super(context);
         setWillNotDraw(false);
 
+        final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
+                attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
         final int gestureFloatingPreviewTextSize = keyboardViewAttr.getDimensionPixelSize(
                 R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0);
         mGestureFloatingPreviewTextColor = keyboardViewAttr.getColor(
@@ -117,6 +127,7 @@
                 R.styleable.KeyboardView_gesturePreviewTrailColor, 0);
         final int gesturePreviewTrailWidth = keyboardViewAttr.getDimensionPixelSize(
                 R.styleable.KeyboardView_gesturePreviewTrailWidth, 0);
+        keyboardViewAttr.recycle();
 
         mGesturePaint = new Paint();
         mGesturePaint.setAntiAlias(true);
@@ -146,31 +157,30 @@
     public void invalidatePointer(PointerTracker tracker) {
         synchronized (mPointers) {
             mPointers.put(tracker.mPointerId, tracker);
-            // TODO: Should narrow the invalidate region.
-            invalidate();
         }
+        mLastPointerX = tracker.getLastX();
+        mLastPointerY = tracker.getLastY();
+        // TODO: Should narrow the invalidate region.
+        invalidate();
     }
 
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        synchronized (mPointers) {
-            canvas.translate(mXOrigin, mYOrigin);
-            final int trackerCount = mPointers.size();
-            boolean hasDrawnFloatingPreviewText = false;
-            for (int index = 0; index < trackerCount; index++) {
-                final PointerTracker tracker = mPointers.valueAt(index);
-                if (mDrawsGesturePreviewTrail) {
+        canvas.translate(mXOrigin, mYOrigin);
+        if (mDrawsGesturePreviewTrail) {
+            synchronized (mPointers) {
+                final int trackerCount = mPointers.size();
+                for (int index = 0; index < trackerCount; index++) {
+                    final PointerTracker tracker = mPointers.valueAt(index);
                     tracker.drawGestureTrail(canvas, mGesturePaint);
                 }
-                // TODO: Figure out more cleaner way to draw gesture preview text.
-                if (mDrawsGestureFloatingPreviewText && !hasDrawnFloatingPreviewText) {
-                    drawGestureFloatingPreviewText(canvas, tracker, mGestureFloatingPreviewText);
-                    hasDrawnFloatingPreviewText = true;
-                }
             }
-            canvas.translate(-mXOrigin, -mYOrigin);
         }
+        if (mDrawsGestureFloatingPreviewText) {
+            drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText);
+        }
+        canvas.translate(-mXOrigin, -mYOrigin);
     }
 
     public void setGestureFloatingPreviewText(String gestureFloatingPreviewText) {
@@ -186,15 +196,16 @@
         mDrawingHandler.cancelAllMessages();
     }
 
-    private void drawGestureFloatingPreviewText(Canvas canvas, PointerTracker tracker,
-            String gestureFloatingPreviewText) {
+    private void drawGestureFloatingPreviewText(Canvas canvas, String gestureFloatingPreviewText) {
         if (TextUtils.isEmpty(gestureFloatingPreviewText)) {
             return;
         }
 
         final Paint paint = mTextPaint;
-        final int lastX = tracker.getLastX();
-        final int lastY = tracker.getLastY();
+        // TODO: Figure out how we should deal with the floating preview text with multiple moving
+        // fingers.
+        final int lastX = mLastPointerX;
+        final int lastY = mLastPointerY;
         final int textSize = (int)paint.getTextSize();
         final int canvasWidth = canvas.getWidth();
 
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index 0481668..01ba300 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -39,7 +39,6 @@
         }
         final CharSequence lowerCasedWord = word.toString().toLowerCase();
         for (final String key : dictionaries.keySet()) {
-            if (key.equals(Dictionary.TYPE_WHITELIST)) continue;
             final Dictionary dictionary = dictionaries.get(key);
             // It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
             // managing to get null in here. Presumably the language is changing to a language with
@@ -64,7 +63,6 @@
         }
         int maxFreq = -1;
         for (final String key : dictionaries.keySet()) {
-            if (key.equals(Dictionary.TYPE_WHITELIST)) continue;
             final Dictionary dictionary = dictionaries.get(key);
             if (null == dictionary) continue;
             final int tempFreq = dictionary.getFrequency(word);
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 726c44c..2d9e0ce 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -64,18 +64,21 @@
 
     private final SparseArray<DicTraverseSession> mDicTraverseSessions =
             new SparseArray<DicTraverseSession>();
+
+    // TODO: There should be a way to remove used DicTraverseSession objects from
+    // {@code mDicTraverseSessions}.
     private DicTraverseSession getTraverseSession(int traverseSessionId) {
-        DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
-        if (traverseSession == null) {
-            synchronized(mDicTraverseSessions) {
+        synchronized(mDicTraverseSessions) {
+            DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
+            if (traverseSession == null) {
                 traverseSession = mDicTraverseSessions.get(traverseSessionId);
                 if (traverseSession == null) {
                     traverseSession = new DicTraverseSession(mLocale, mNativeDict);
                     mDicTraverseSessions.put(traverseSessionId, traverseSession);
                 }
             }
+            return traverseSession;
         }
-        return traverseSession;
     }
 
     /**
@@ -209,18 +212,20 @@
     }
 
     @Override
-    public synchronized void close() {
-        for (int i = 0; i < mDicTraverseSessions.size(); ++i) {
-            final int key = mDicTraverseSessions.keyAt(i);
-            final DicTraverseSession traverseSession = mDicTraverseSessions.get(key);
-            if (traverseSession != null) {
-                traverseSession.close();
+    public void close() {
+        synchronized (mDicTraverseSessions) {
+            final int sessionsSize = mDicTraverseSessions.size();
+            for (int index = 0; index < sessionsSize; ++index) {
+                final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index);
+                if (traverseSession != null) {
+                    traverseSession.close();
+                }
             }
         }
         closeInternal();
     }
 
-    private void closeInternal() {
+    private synchronized void closeInternal() {
         if (mNativeDict != 0) {
             closeNative(mNativeDict);
             mNativeDict = 0;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 063243e..dd11aaa 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin;
 
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -23,6 +25,7 @@
 import android.util.Log;
 
 import java.io.File;
+import java.io.RandomAccessFile;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Locale;
@@ -51,6 +54,9 @@
     private static final String MAIN_DICTIONARY_CATEGORY = "main";
     public static final String ID_CATEGORY_SEPARATOR = ":";
 
+    // The key considered to read the version attribute in a dictionary file.
+    private static String VERSION_KEY = "version";
+
     // Prevents this from being instantiated
     private BinaryDictionaryGetter() {}
 
@@ -336,6 +342,42 @@
         return MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
     }
 
+    // ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason
+    // for this is, since those do not include whitelist entries, the new code with an old version
+    // of the dictionary would lose whitelist functionality.
+    private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) {
+        // Only for English - other languages didn't have a whitelist, hence this
+        // ad-hock ## HACK ##
+        if (!Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) return true;
+
+        try {
+            // Read the version of the file
+            final RandomAccessFile raf = new RandomAccessFile(f, "r");
+            final int magic = raf.readInt();
+            if (magic != BinaryDictInputOutput.VERSION_2_MAGIC_NUMBER) {
+                return false;
+            }
+            final int formatVersion = raf.readInt();
+            final int headerSize = raf.readInt();
+            final HashMap<String, String> options = new HashMap<String, String>();
+            BinaryDictInputOutput.populateOptionsFromFile(raf, headerSize, options);
+            final String version = options.get(VERSION_KEY);
+            if (null == version) {
+                // No version in the options : the format is unexpected
+                return false;
+            }
+            // Version 18 is the first one to include the whitelist
+            // Obviously this is a big ## HACK ##
+            return Integer.parseInt(version) >= 18;
+        } catch (java.io.FileNotFoundException e) {
+            return false;
+        } catch (java.io.IOException e) {
+            return false;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
     /**
      * Returns a list of file addresses for a given locale, trying relevant methods in order.
      *
@@ -366,14 +408,15 @@
         // cachedWordLists may not be null, see doc for getCachedDictionaryList
         for (final File f : cachedWordLists) {
             final String wordListId = getWordListIdFromFileName(f.getName());
-            if (isMainWordListId(wordListId)) {
+            final boolean canUse = f.canRead() && hackCanUseDictionaryFile(locale, f);
+            if (canUse && isMainWordListId(wordListId)) {
                 foundMainDict = true;
             }
             if (!dictPackSettings.isWordListActive(wordListId)) continue;
-            if (f.canRead()) {
+            if (canUse) {
                 fileList.add(AssetFileAddress.makeFromFileName(f.getPath()));
             } else {
-                Log.e(TAG, "Found a cached dictionary file but cannot read it");
+                Log.e(TAG, "Found a cached dictionary file but cannot read or use it");
             }
         }
 
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 2565dfc..88d0c09 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -42,7 +42,6 @@
     public static final String TYPE_USER = "user";
     // User history dictionary internal to LatinIME.
     public static final String TYPE_USER_HISTORY = "history";
-    public static final String TYPE_WHITELIST ="whitelist";
     protected final String mDictType;
 
     public Dictionary(final String dictType) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a4c82c9..aa2f185 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1669,6 +1669,7 @@
 
         Utils.Stats.onSeparator((char)primaryCode, x, y);
 
+        mHandler.postUpdateShiftState();
         return didAutoCorrect;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 1f43c6d..e931408 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -50,7 +50,6 @@
 
     private Dictionary mMainDictionary;
     private ContactsBinaryDictionary mContactsDict;
-    private WhitelistDictionary mWhiteListDictionary;
     private final ConcurrentHashMap<String, Dictionary> mDictionaries =
             new ConcurrentHashMap<String, Dictionary>();
 
@@ -74,21 +73,11 @@
         mLocale = locale;
         mMainDictionary = mainDict;
         addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict);
-        initWhitelistAndAutocorrectAndPool(context, locale);
-    }
-
-    private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
-        mWhiteListDictionary = new WhitelistDictionary(context, locale);
-        addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary);
     }
 
     private void initAsynchronously(final Context context, final Locale locale,
             final SuggestInitializationListener listener) {
         resetMainDict(context, locale, listener);
-
-        // TODO: read the whitelist and init the pool asynchronously too.
-        // initPool should be done asynchronously now that the pool is thread-safe.
-        initWhitelistAndAutocorrectAndPool(context, locale);
     }
 
     private static void addOrReplaceDictionary(
@@ -216,15 +205,6 @@
                     wordComposerForLookup, prevWordForBigram, proximityInfo));
         }
 
-        final CharSequence whitelistedWordFromWhitelistDictionary =
-                mWhiteListDictionary.getWhitelistedWord(consideredWord);
-        if (whitelistedWordFromWhitelistDictionary != null) {
-            // MAX_SCORE ensures this will be considered strong enough to be auto-corrected
-            suggestionsSet.add(new SuggestedWordInfo(whitelistedWordFromWhitelistDictionary,
-                    SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST,
-                    Dictionary.TYPE_WHITELIST));
-        }
-
         final CharSequence whitelistedWord;
         if (suggestionsSet.isEmpty()) {
             whitelistedWord = null;
@@ -234,11 +214,6 @@
             whitelistedWord = suggestionsSet.first().mWord;
         }
 
-        // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
-        // but still autocorrected from - in the case the whitelist only capitalizes the word.
-        // The whitelist should be case-insensitive, so it's not possible to be consistent with
-        // a boolean flag. Right now this is handled with a slight hack in
-        // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
         final boolean allowsToBeAutoCorrected = (null != whitelistedWord
                 && !whitelistedWord.equals(consideredWord))
                 || AutoCorrection.isNotAWord(mDictionaries, consideredWord,
@@ -320,9 +295,10 @@
 
         // At second character typed, search the unigrams (scores being affected by bigrams)
         for (final String key : mDictionaries.keySet()) {
-            // Skip UserUnigramDictionary and WhitelistDictionary to lookup
-            if (key.equals(Dictionary.TYPE_USER_HISTORY)
-                    || key.equals(Dictionary.TYPE_WHITELIST)) {
+            // Skip User history dictionary for lookup
+            // TODO: The user history dictionary should just override getSuggestionsWithSessionId
+            // to make sure it doesn't return anything and we should remove this test
+            if (key.equals(Dictionary.TYPE_USER_HISTORY)) {
                 continue;
             }
             final Dictionary dictionary = mDictionaries.get(key);
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
deleted file mode 100644
index 14476dc..0000000
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Locale;
-
-public class WhitelistDictionary extends ExpandableDictionary {
-
-    private static final boolean DBG = LatinImeLogger.sDBG;
-    private static final String TAG = WhitelistDictionary.class.getSimpleName();
-
-    private final HashMap<String, Pair<Integer, String>> mWhitelistWords =
-            new HashMap<String, Pair<Integer, String>>();
-
-    // TODO: Conform to the async load contact of ExpandableDictionary
-    public WhitelistDictionary(final Context context, final Locale locale) {
-        super(context, Dictionary.TYPE_WHITELIST);
-        // TODO: Move whitelist dictionary into main dictionary.
-        final RunInLocale<Void> job = new RunInLocale<Void>() {
-            @Override
-            protected Void job(Resources res) {
-                initWordlist(res.getStringArray(R.array.wordlist_whitelist));
-                return null;
-            }
-        };
-        job.runInLocale(context.getResources(), locale);
-    }
-
-    private void initWordlist(String[] wordlist) {
-        mWhitelistWords.clear();
-        final int N = wordlist.length;
-        if (N % 3 != 0) {
-            if (DBG) {
-                Log.d(TAG, "The number of the whitelist is invalid.");
-            }
-            return;
-        }
-        try {
-            for (int i = 0; i < N; i += 3) {
-                final int score = Integer.valueOf(wordlist[i]);
-                final String before = wordlist[i + 1];
-                final String after = wordlist[i + 2];
-                if (before != null && after != null) {
-                    mWhitelistWords.put(
-                            before.toLowerCase(), new Pair<Integer, String>(score, after));
-                    addWord(after, null /* shortcut */, score);
-                }
-            }
-        } catch (NumberFormatException e) {
-            if (DBG) {
-                Log.d(TAG, "The score of the word is invalid.");
-            }
-        }
-    }
-
-    public String getWhitelistedWord(String before) {
-        if (before == null) return null;
-        final String lowerCaseBefore = before.toLowerCase();
-        if(mWhitelistWords.containsKey(lowerCaseBefore)) {
-            if (DBG) {
-                Log.d(TAG, "--- found whitelistedWord: " + lowerCaseBefore);
-            }
-            return mWhitelistWords.get(lowerCaseBefore).second;
-        }
-        return null;
-    }
-
-    @Override
-    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
-            final CharSequence prevWord, final ProximityInfo proximityInfo) {
-        // Whitelist does not supply any suggestions or predictions.
-        return null;
-    }
-
-    // See LatinIME#updateSuggestions. This breaks in the (queer) case that the whitelist
-    // lists that word a should autocorrect to word b, and word c would autocorrect to
-    // an upper-cased version of a. In this case, the way this return value is used would
-    // remove the first candidate when the user typed the upper-cased version of A.
-    // Example : abc -> def  and  xyz -> Abc
-    // A user typing Abc would experience it being autocorrected to something else (not
-    // necessarily def).
-    // There is no such combination in the whitelist at the time and there probably won't
-    // ever be - it doesn't make sense. But still.
-    public boolean shouldForciblyAutoCorrectFrom(CharSequence word) {
-        if (TextUtils.isEmpty(word)) return false;
-        final String correction = getWhitelistedWord(word.toString());
-        if (TextUtils.isEmpty(correction)) return false;
-        return !correction.equals(word);
-    }
-
-    // Leave implementation of getWords and isValidWord to the superclass.
-    // The words have been added to the ExpandableDictionary with addWord() inside initWordlist.
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index 7f04233..b23b7db 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -124,7 +124,7 @@
      */
 
     private static final int VERSION_1_MAGIC_NUMBER = 0x78B1;
-    private static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
+    public static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
     private static final int MINIMUM_SUPPORTED_VERSION = 1;
     private static final int MAXIMUM_SUPPORTED_VERSION = 2;
     private static final int NOT_A_VERSION_NUMBER = -1;
@@ -1328,6 +1328,21 @@
     }
 
     /**
+     * Reads options from a file and populate a map with their contents.
+     *
+     * The file is read at the current file pointer, so the caller must take care the pointer
+     * is in the right place before calling this.
+     */
+    public static void populateOptionsFromFile(final RandomAccessFile source, final long headerSize,
+            final HashMap<String, String> options) throws IOException {
+        while (source.getFilePointer() < headerSize) {
+            final String key = CharEncoding.readString(source);
+            final String value = CharEncoding.readString(source);
+            options.put(key, value);
+        }
+    }
+
+    /**
      * Reads a random access file and returns the memory representation of the dictionary.
      *
      * This high-level method takes a binary file and reads its contents, populating a
@@ -1358,11 +1373,7 @@
         } else {
             headerSize = (source.readUnsignedByte() << 24) + (source.readUnsignedByte() << 16)
                     + (source.readUnsignedByte() << 8) + source.readUnsignedByte();
-            while (source.getFilePointer() < headerSize) {
-                final String key = CharEncoding.readString(source);
-                final String value = CharEncoding.readString(source);
-                options.put(key, value);
-            }
+            populateOptionsFromFile(source, headerSize, options);
             source.seek(headerSize);
         }
 
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 3bdfe1f..d05dc02 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -35,7 +35,6 @@
 import com.android.inputmethod.latin.SynchronouslyLoadedContactsBinaryDictionary;
 import com.android.inputmethod.latin.SynchronouslyLoadedUserBinaryDictionary;
 import com.android.inputmethod.latin.UserBinaryDictionary;
-import com.android.inputmethod.latin.WhitelistDictionary;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -67,8 +66,6 @@
             Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
     private Map<String, UserBinaryDictionary> mUserDictionaries =
             Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>());
-    private Map<String, Dictionary> mWhitelistDictionaries =
-            Collections.synchronizedMap(new TreeMap<String, Dictionary>());
     private ContactsBinaryDictionary mContactsDictionary;
 
     // The threshold for a candidate to be offered as a suggestion.
@@ -366,8 +363,6 @@
         final Map<String, UserBinaryDictionary> oldUserDictionaries = mUserDictionaries;
         mUserDictionaries =
                 Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>());
-        final Map<String, Dictionary> oldWhitelistDictionaries = mWhitelistDictionaries;
-        mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
         new Thread("spellchecker_close_dicts") {
             @Override
             public void run() {
@@ -377,9 +372,6 @@
                 for (Dictionary dict : oldUserDictionaries.values()) {
                     dict.close();
                 }
-                for (Dictionary dict : oldWhitelistDictionaries.values()) {
-                    dict.close();
-                }
                 synchronized (mUseContactsLock) {
                     if (null != mContactsDictionary) {
                         // The synchronously loaded contacts dictionary should have been in one
@@ -423,12 +415,6 @@
             mUserDictionaries.put(localeStr, userDictionary);
         }
         dictionaryCollection.addDictionary(userDictionary);
-        Dictionary whitelistDictionary = mWhitelistDictionaries.get(localeStr);
-        if (null == whitelistDictionary) {
-            whitelistDictionary = new WhitelistDictionary(this, locale);
-            mWhitelistDictionaries.put(localeStr, whitelistDictionary);
-        }
-        dictionaryCollection.addDictionary(whitelistDictionary);
         synchronized (mUseContactsLock) {
             if (mUseContactsDictionary) {
                 if (null == mContactsDictionary) {