Merge "Fix crash on adding a word in the user dictionary."
diff --git a/java/res/values-sw600dp/donottranslate.xml b/java/res/values-sw600dp/donottranslate.xml
deleted file mode 100644
index 6d94c28..0000000
--- a/java/res/values-sw600dp/donottranslate.xml
+++ /dev/null
@@ -1,23 +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">
-    <!--  Default value of the visibility of the suggestion strip -->
-    <string name="prefs_suggestion_visibility_default_value" translatable="false">1</string>
-</resources>
diff --git a/java/res/values-sw768dp/donottranslate.xml b/java/res/values-sw768dp/donottranslate.xml
deleted file mode 100644
index 672dea5..0000000
--- a/java/res/values-sw768dp/donottranslate.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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">
-    <!--  Default value of the visibility of the suggestion strip -->
-    <string name="prefs_suggestion_visibility_default_value" translatable="false">2</string>
-</resources>
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
index b5b6232..b807dd3 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -37,11 +37,9 @@
 import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
 import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 
-import java.util.ArrayList;
 import java.util.WeakHashMap;
 
 /**
@@ -74,11 +72,6 @@
     /** Listener for {@link KeyboardActionListener}. */
     private KeyboardActionListener mKeyboardActionListener;
 
-    private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>();
-
-    // TODO: Let the PointerTracker class manage this pointer queue
-    private final PointerTrackerQueue mPointerQueue;
-
     private final boolean mHasDistinctMultitouch;
     private int mOldPointerCount = 1;
     private int mOldKeyIndex;
@@ -251,7 +244,7 @@
                 .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
         mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
 
-        mPointerQueue = mHasDistinctMultitouch ? new PointerTrackerQueue() : null;
+        PointerTracker.init(mHasDistinctMultitouch, getContext());
     }
 
     public void startIgnoringDoubleTap() {
@@ -261,9 +254,7 @@
 
     public void setKeyboardActionListener(KeyboardActionListener listener) {
         mKeyboardActionListener = listener;
-        for (PointerTracker tracker : mPointerTrackers) {
-            tracker.setKeyboardActionListener(listener);
-        }
+        PointerTracker.setKeyboardActionListener(listener);
     }
 
     /**
@@ -306,17 +297,15 @@
     @Override
     public void setKeyboard(Keyboard keyboard) {
         if (getKeyboard() != null) {
-            dismissAllKeyPreviews();
+            PointerTracker.dismissAllKeyPreviews();
         }
         // Remove any pending messages, except dismissing preview
         mKeyTimerHandler.cancelKeyTimers();
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(
                 keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
-        for (PointerTracker tracker : mPointerTrackers) {
-            tracker.setKeyDetector(mKeyDetector);
-        }
         mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
+        PointerTracker.setKeyDetector(mKeyDetector);
         mPopupPanelCache.clear();
     }
 
@@ -345,14 +334,6 @@
         return mKeyDetector.isProximityCorrectionEnabled();
     }
 
-    // TODO: clean up this method.
-    private void dismissAllKeyPreviews() {
-        for (PointerTracker tracker : mPointerTrackers) {
-            tracker.setReleasedKeyGraphics();
-            dismissKeyPreview(tracker);
-        }
-    }
-
     @Override
     public void cancelAllMessages() {
         mKeyTimerHandler.cancelAllMessages();
@@ -451,29 +432,14 @@
     }
 
     private PointerTracker getPointerTracker(final int id) {
-        final ArrayList<PointerTracker> pointers = mPointerTrackers;
-        final KeyboardActionListener listener = mKeyboardActionListener;
-
-        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
-        for (int i = pointers.size(); i <= id; i++) {
-            final PointerTracker tracker =
-                new PointerTracker(i, getContext(), mKeyTimerHandler, mKeyDetector, this,
-                        mPointerQueue);
-            if (listener != null)
-                tracker.setKeyboardActionListener(listener);
-            pointers.add(tracker);
-        }
-
-        return pointers.get(id);
+        return PointerTracker.getPointerTracker(id, this);
     }
 
     public boolean isInSlidingKeyInput() {
         if (mPopupPanel != null) {
             return true;
-        } else if (mPointerQueue != null) {
-            return mPointerQueue.isInSlidingKeyInput();
         } else {
-            return getPointerTracker(0).isInSlidingKeyInput();
+            return PointerTracker.isAnyInSlidingKeyInput();
         }
     }
 
@@ -499,7 +465,7 @@
         // Gesture detector must be enabled only when mini-keyboard is not on the screen.
         if (mPopupPanel == null && mGestureDetector != null
                 && mGestureDetector.onTouchEvent(me)) {
-            dismissAllKeyPreviews();
+            PointerTracker.dismissAllKeyPreviews();
             mKeyTimerHandler.cancelKeyTimers();
             return true;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index d23fb4a..aa2f3af 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -25,6 +25,7 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -77,23 +78,24 @@
         public void cancelKeyTimers();
     }
 
-    public final int mPointerId;
-
+    private static KeyboardSwitcher sKeyboardSwitcher;
+    private static boolean sConfigSlidingKeyInputEnabled;
     // Timing constants
-    private final int mDelayBeforeKeyRepeatStart;
-    private final int mLongPressKeyTimeout;
-    private final int mLongPressShiftKeyTimeout;
+    private static int sDelayBeforeKeyRepeatStart;
+    private static int sLongPressKeyTimeout;
+    private static int sLongPressShiftKeyTimeout;
+    private static int sTouchNoiseThresholdMillis;
+    private static int sTouchNoiseThresholdDistanceSquared;
+
+    private static final List<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
+    private static PointerTrackerQueue sPointerTrackerQueue;
+
+    public final int mPointerId;
 
     private DrawingProxy mDrawingProxy;
     private TimerProxy mTimerProxy;
-    private final PointerTrackerQueue mPointerTrackerQueue;
     private KeyDetector mKeyDetector;
     private KeyboardActionListener mListener = EMPTY_LISTENER;
-    private final KeyboardSwitcher mKeyboardSwitcher;
-    private final boolean mConfigSlidingKeyInputEnabled;
-
-    private final int mTouchNoiseThresholdMillis;
-    private final int mTouchNoiseThresholdDistanceSquared;
 
     private Keyboard mKeyboard;
     private List<Key> mKeys;
@@ -123,7 +125,7 @@
     private boolean mIsRepeatableKey;
 
     // true if this pointer is in sliding key input
-    private boolean mIsInSlidingKeyInput;
+    boolean mIsInSlidingKeyInput;
 
     // true if sliding key is allowed.
     private boolean mIsAllowedSlidingKeyInput;
@@ -135,7 +137,7 @@
     // true if this pointer is in sliding language switch
     private boolean mIsInSlidingLanguageSwitch;
     private int mSpaceKeyIndex;
-    private final SubtypeSwitcher mSubtypeSwitcher;
+    private static SubtypeSwitcher sSubtypeSwitcher;
 
     // Empty {@link KeyboardActionListener}
     private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() {
@@ -151,31 +153,72 @@
         public void onCancelInput() {}
     };
 
-    public PointerTracker(int id, Context context, TimerProxy timerProxy, KeyDetector keyDetector,
-            DrawingProxy drawingProxy, PointerTrackerQueue queue) {
-        if (drawingProxy == null || timerProxy == null || keyDetector == null)
-            throw new NullPointerException();
-        mPointerId = id;
-        mDrawingProxy = drawingProxy;
-        mTimerProxy = timerProxy;
-        mPointerTrackerQueue = queue;  // This is null for non-distinct multi-touch device.
-        setKeyDetectorInner(keyDetector);
-        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
+    public static void init(boolean hasDistinctMultitouch, Context context) {
+        if (hasDistinctMultitouch) {
+            sPointerTrackerQueue = new PointerTrackerQueue();
+        } else {
+            sPointerTrackerQueue = null;
+        }
+
         final Resources res = context.getResources();
-        mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
-        mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
-        mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout);
-        mLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout);
-        mTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis);
+        sConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
+        sDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
+        sLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout);
+        sLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout);
+        sTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis);
         final float touchNoiseThresholdDistance = res.getDimension(
                 R.dimen.config_touch_noise_threshold_distance);
-        mTouchNoiseThresholdDistanceSquared = (int)(
+        sTouchNoiseThresholdDistanceSquared = (int)(
                 touchNoiseThresholdDistance * touchNoiseThresholdDistance);
-        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
+        sKeyboardSwitcher = KeyboardSwitcher.getInstance();
+        sSubtypeSwitcher = SubtypeSwitcher.getInstance();
     }
 
-    public void setKeyboardActionListener(KeyboardActionListener listener) {
-        mListener = listener;
+    public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
+        final List<PointerTracker> trackers = sTrackers;
+
+        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
+        for (int i = trackers.size(); i <= id; i++) {
+            final PointerTracker tracker = new PointerTracker(i, handler);
+            trackers.add(tracker);
+        }
+
+        return trackers.get(id);
+    }
+
+    public static boolean isAnyInSlidingKeyInput() {
+        return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
+    }
+
+    public static void setKeyboardActionListener(KeyboardActionListener listener) {
+        for (final PointerTracker tracker : sTrackers) {
+            tracker.mListener = listener;
+        }
+    }
+
+    public static void setKeyDetector(KeyDetector keyDetector) {
+        for (final PointerTracker tracker : sTrackers) {
+            tracker.setKeyDetectorInner(keyDetector);
+            // Mark that keyboard layout has been changed.
+            tracker.mKeyboardLayoutHasBeenChanged = true;
+        }
+    }
+
+    public static void dismissAllKeyPreviews() {
+        for (final PointerTracker tracker : sTrackers) {
+            tracker.setReleasedKeyGraphics();
+            tracker.dismissKeyPreview();
+        }
+    }
+
+    public PointerTracker(int id, KeyEventHandler handler) {
+        if (handler == null)
+            throw new NullPointerException();
+        mPointerId = id;
+        setKeyDetectorInner(handler.getKeyDetector());
+        mListener = handler.getKeyboardActionListener();
+        mDrawingProxy = handler.getDrawingProxy();
+        mTimerProxy = handler.getTimerProxy();
     }
 
     // Returns true if keyboard has been changed by this callback.
@@ -243,14 +286,6 @@
         mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
     }
 
-    public void setKeyDetector(KeyDetector keyDetector) {
-        if (keyDetector == null)
-            throw new NullPointerException();
-        setKeyDetectorInner(keyDetector);
-        // Mark that keyboard layout has been changed.
-        mKeyboardLayoutHasBeenChanged = true;
-    }
-
     public boolean isInSlidingKeyInput() {
         return mIsInSlidingKeyInput;
     }
@@ -365,11 +400,11 @@
         setKeyDetectorInner(handler.getKeyDetector());
         // Naive up-to-down noise filter.
         final long deltaT = eventTime - mUpTime;
-        if (deltaT < mTouchNoiseThresholdMillis) {
+        if (deltaT < sTouchNoiseThresholdMillis) {
             final int dx = x - mLastX;
             final int dy = y - mLastY;
             final int distanceSquared = (dx * dx + dy * dy);
-            if (distanceSquared < mTouchNoiseThresholdDistanceSquared) {
+            if (distanceSquared < sTouchNoiseThresholdDistanceSquared) {
                 if (DEBUG_MODE)
                     Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
                             + " distance=" + distanceSquared);
@@ -378,7 +413,7 @@
             }
         }
 
-        final PointerTrackerQueue queue = mPointerTrackerQueue;
+        final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
             if (isOnModifierKey(x, y)) {
                 // Before processing a down event of modifier key, all pointers already being
@@ -394,7 +429,7 @@
         int keyIndex = onDownKey(x, y, eventTime);
         // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
         // from modifier key, or 3) this pointer is on mini-keyboard.
-        mIsAllowedSlidingKeyInput = mConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
+        mIsAllowedSlidingKeyInput = sConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
                 || mKeyDetector instanceof MiniKeyboardKeyDetector;
         mKeyboardLayoutHasBeenChanged = false;
         mKeyAlreadyProcessed = false;
@@ -495,8 +530,8 @@
             else if (isSpaceKey(keyIndex) && !mIsInSlidingLanguageSwitch
                     && mKeyboard instanceof LatinKeyboard) {
                 final LatinKeyboard keyboard = ((LatinKeyboard)mKeyboard);
-                if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()
-                        && mSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
+                if (sSubtypeSwitcher.useSpacebarLanguageSwitcher()
+                        && sSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
                     final int diff = x - mKeyX;
                     if (keyboard.shouldTriggerSpacebarSlidingLanguageSwitch(diff)) {
                         // Detect start sliding language switch.
@@ -505,7 +540,7 @@
                         keyboard.updateSpacebarPreviewIcon(diff);
                         // Display spacebar slide language switcher.
                         showKeyPreview(keyIndex);
-                        final PointerTrackerQueue queue = mPointerTrackerQueue;
+                        final PointerTrackerQueue queue = sPointerTrackerQueue;
                         if (queue != null)
                             queue.releaseAllPointersExcept(this, eventTime, true);
                     }
@@ -533,7 +568,7 @@
         if (DEBUG_EVENT)
             printTouchEvent("onUpEvent  :", x, y, eventTime);
 
-        final PointerTrackerQueue queue = mPointerTrackerQueue;
+        final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
             if (isModifier()) {
                 // Before processing an up event of modifier key, all pointers already being
@@ -600,7 +635,7 @@
         mKeyAlreadyProcessed = true;
         setReleasedKeyGraphics();
         dismissKeyPreview();
-        final PointerTrackerQueue queue = mPointerTrackerQueue;
+        final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
             queue.remove(this);
         }
@@ -610,7 +645,7 @@
         if (DEBUG_EVENT)
             printTouchEvent("onCancelEvt:", x, y, eventTime);
 
-        final PointerTrackerQueue queue = mPointerTrackerQueue;
+        final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
             queue.releaseAllPointersExcept(this, eventTime, true);
             queue.remove(this);
@@ -631,7 +666,7 @@
         if (key != null && key.mRepeatable) {
             dismissKeyPreview();
             onRepeatKey(keyIndex);
-            mTimerProxy.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
+            mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, keyIndex, this);
             mIsRepeatableKey = true;
         } else {
             mIsRepeatableKey = false;
@@ -685,16 +720,16 @@
     private void startLongPressTimer(int keyIndex) {
         Key key = getKey(keyIndex);
         if (key.mCode == Keyboard.CODE_SHIFT) {
-            mTimerProxy.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this);
+            mTimerProxy.startLongPressShiftTimer(sLongPressShiftKeyTimeout, keyIndex, this);
         } else if (key.hasUppercaseLetter() && mKeyboard.isManualTemporaryUpperCase()) {
             // We need not start long press timer on the key which has manual temporary upper case
             // code defined and the keyboard is in manual temporary upper case mode.
             return;
-        } else if (mKeyboardSwitcher.isInMomentarySwitchState()) {
+        } else if (sKeyboardSwitcher.isInMomentarySwitchState()) {
             // We use longer timeout for sliding finger input started from the symbols mode key.
-            mTimerProxy.startLongPressTimer(mLongPressKeyTimeout * 3, keyIndex, this);
+            mTimerProxy.startLongPressTimer(sLongPressKeyTimeout * 3, keyIndex, this);
         } else {
-            mTimerProxy.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this);
+            mTimerProxy.startLongPressTimer(sLongPressKeyTimeout, keyIndex, this);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index f87cd86..545b27f 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -63,7 +63,7 @@
         mQueue.remove(tracker);
     }
 
-    public boolean isInSlidingKeyInput() {
+    public boolean isAnyInSlidingKeyInput() {
         for (final PointerTracker tracker : mQueue) {
             if (tracker.isInSlidingKeyInput())
                 return true;