am d36ad739: Merge "New IME assets" into ics-factoryrom

* commit 'd36ad739a4e5cef0e016b7f5c3fe6fa983a958a6':
  New IME assets
diff --git a/java/res/drawable-hdpi/more_suggestions_hint.png b/java/res/drawable-hdpi/more_suggestions_hint.png
deleted file mode 100644
index 9360475..0000000
--- a/java/res/drawable-hdpi/more_suggestions_hint.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/more_suggestions_hint_holo.png b/java/res/drawable-hdpi/more_suggestions_hint_holo.png
deleted file mode 100644
index 9360475..0000000
--- a/java/res/drawable-hdpi/more_suggestions_hint_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/more_suggestions_hint.png b/java/res/drawable-mdpi/more_suggestions_hint.png
deleted file mode 100644
index 7352810..0000000
--- a/java/res/drawable-mdpi/more_suggestions_hint.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/more_suggestions_hint_holo.png b/java/res/drawable-mdpi/more_suggestions_hint_holo.png
deleted file mode 100644
index 7352810..0000000
--- a/java/res/drawable-mdpi/more_suggestions_hint_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/more_suggestions_hint.png b/java/res/drawable-xhdpi/more_suggestions_hint.png
deleted file mode 100644
index 35fb420..0000000
--- a/java/res/drawable-xhdpi/more_suggestions_hint.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/more_suggestions_hint_holo.png b/java/res/drawable-xhdpi/more_suggestions_hint_holo.png
deleted file mode 100644
index 35fb420..0000000
--- a/java/res/drawable-xhdpi/more_suggestions_hint_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_key_gingerbread.xml b/java/res/drawable/btn_keyboard_key_gingerbread.xml
index 4a113a8..5b4399e 100644
--- a/java/res/drawable/btn_keyboard_key_gingerbread.xml
+++ b/java/res/drawable/btn_keyboard_key_gingerbread.xml
@@ -23,6 +23,13 @@
     <item android:state_single="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal" />
 
+    <!-- Action keys. -->
+
+    <item android:state_active="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_dark_pressed" />
+    <item android:state_active="true"
+          android:drawable="@drawable/btn_keyboard_key_dark_normal" />
+
     <!-- Toggle keys. Use checkable/checked state. -->
 
     <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true"
@@ -34,7 +41,7 @@
     <item android:state_checkable="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal_off" />
 
-    <!-- Normal keys -->
+    <!-- Normal keys. -->
 
     <item android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_light_pressed" />
diff --git a/java/res/drawable/btn_keyboard_key_stone.xml b/java/res/drawable/btn_keyboard_key_stone.xml
index 27932e8..9bc3f18 100644
--- a/java/res/drawable/btn_keyboard_key_stone.xml
+++ b/java/res/drawable/btn_keyboard_key_stone.xml
@@ -23,6 +23,13 @@
     <item android:state_single="true"
           android:drawable="@drawable/btn_keyboard_key_normal_stone" />
 
+    <!-- Action keys. -->
+
+    <item android:state_active="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+    <item android:state_active="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_stone" />
+
     <!-- Toggle keys. Use checkable/checked state. -->
 
     <item android:state_checkable="true" android:state_checked="true"
@@ -35,7 +42,7 @@
     <item android:state_checkable="true"
           android:drawable="@drawable/btn_keyboard_key_normal_off_stone" />
 
-    <!-- Normal keys -->
+    <!-- Normal keys. -->
 
     <item android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index d02b4ea..e1bdecf 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -76,4 +76,5 @@
     <dimen name="suggestion_min_width">0.3in</dimen>
     <dimen name="suggestion_padding">12dip</dimen>
     <dimen name="suggestion_text_size">22dip</dimen>
+    <dimen name="more_suggestions_hint_text_size">33dip</dimen>
 </resources>
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index bfc2593..b825b14 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -79,4 +79,5 @@
     <dimen name="suggestion_min_width">46dip</dimen>
     <dimen name="suggestion_padding">8dip</dimen>
     <dimen name="suggestion_text_size">22dip</dimen>
+    <dimen name="more_suggestions_hint_text_size">33dip</dimen>
 </resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 6e9461a..e40cc95 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -132,7 +132,6 @@
         <attr name="alphaObsoleted" format="integer" />
         <attr name="suggestionsCountInStrip" format="integer" />
         <attr name="centerSuggestionPercentile" format="integer" />
-        <attr name="moreSuggestionsHint" format="reference" />
         <attr name="maxMoreSuggestionsRow" format="integer" />
         <attr name="minMoreSuggestionsWidth" format="float" />
     </declare-styleable>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 8cfa680..9992478 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -39,6 +39,7 @@
     <bool name="config_default_bigram_prediction">false</bool>
     <bool name="config_default_compat_recorrection_enabled">true</bool>
     <bool name="config_default_sound_enabled">false</bool>
+    <bool name="config_default_vibration_enabled">true</bool>
     <bool name="config_auto_correction_spacebar_led_enabled">true</bool>
     <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
     <bool name="config_show_mini_keyboard_at_touched_point">false</bool>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 8488c77..16cccc6 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -92,6 +92,7 @@
     <dimen name="suggestion_min_width">44dip</dimen>
     <dimen name="suggestion_padding">6dip</dimen>
     <dimen name="suggestion_text_size">18dip</dimen>
+    <dimen name="more_suggestions_hint_text_size">27dip</dimen>
     <integer name="suggestions_count_in_strip">3</integer>
     <integer name="center_suggestion_percentile">36</integer>
 
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 4072ea4..75e22dd 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -31,6 +31,21 @@
     <!-- Word separator list is the union of all symbols except those that are not separators:
     magic_space_swapping_symbols | magic_space_stripping_symbols |
             magic_space_neutral_symbols \ symbols_excluded_from_word_separators -->
+    <!-- Symbol characters list that should switch back to the main layout -->
+    <!--  \u0022: Quotation mark (double quotation mark)
+          \u0027: Apostrophe (single quotation mark)
+          \u2018: Left single quotation mark
+          \u2019: Right single quotation mark
+          \u201a: Single low-9 quotation mark
+          \u201b: Single high-reversed-9 quotation mark
+          \u201c: Left double quotation mark
+          \u201d: Right double quotation mark
+          \u201e: Double low-9 quotation mark
+          \u201f: Double high-reversed-9 quotation mark
+          \u00ab: Left-pointing double angle quotation mark
+          \u00bb: Right-pointing double angle quotation mark  -->
+    <!-- string name="layout_switch_back_symbols">\u0022\u0027\u2018\u2019\u201a\u201b\u201c\u201d\u201e\u201f\u00ab\u00bb</string> -->
+    <string name="layout_switch_back_symbols"></string>
 
     <!-- Label for "switch to more symbol" modifier key.  Must be short to fit on key! -->
     <string name="label_to_more_symbol_key">= \\ &lt;</string>
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
new file mode 100644
index 0000000..2569f23
--- /dev/null
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -0,0 +1,26 @@
+<?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>
+    <string-array name="keypress_vibration_durations" translatable="false">
+        <!-- Build.HARDWARE,duration_in_milliseconds -->
+        <item>herring,5</item>
+        <item>tuna,5</item>
+    </string-array>
+</resources>
diff --git a/java/res/values/keypress-volumes.xml b/java/res/values/keypress-volumes.xml
new file mode 100644
index 0000000..4d0e7a0
--- /dev/null
+++ b/java/res/values/keypress-volumes.xml
@@ -0,0 +1,27 @@
+<?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>
+    <string-array name="keypress_volumes" translatable="false">
+        <!-- Build.HARDWARE,volume -->
+        <item>herring,0.05</item>
+        <item>tuna,0.05</item>
+        <item>stingray,0.04</item>
+    </string-array>
+</resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 6aa9674..7b8bfa8 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -102,7 +102,6 @@
         <item name="alphaObsoleted">50</item>
         <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
         <item name="centerSuggestionPercentile">@integer/center_suggestion_percentile</item>
-        <item name="moreSuggestionsHint">@drawable/more_suggestions_hint</item>
         <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
         <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
     </style>
@@ -284,7 +283,6 @@
         <item name="alphaObsoleted">70</item>
         <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
         <item name="centerSuggestionPercentile">@integer/center_suggestion_percentile</item>
-        <item name="moreSuggestionsHint">@drawable/more_suggestions_hint_holo</item>
         <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
         <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
     </style>
diff --git a/java/res/values/sudden-jumping-touch-event-device-list.xml b/java/res/values/sudden-jumping-touch-event-device-list.xml
index af1eefc..ba828a7 100644
--- a/java/res/values/sudden-jumping-touch-event-device-list.xml
+++ b/java/res/values/sudden-jumping-touch-event-device-list.xml
@@ -20,7 +20,7 @@
 <resources>
     <string-array name="sudden_jumping_touch_event_device_list" translatable="false">
         <!-- Nexus One -->
-        <item>passion</item>
+        <item>mahimahi</item>
         <!-- Droid -->
         <item>sholes</item>
     </string-array>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 43bbd65..24de95f 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -32,6 +32,7 @@
         <CheckBoxPreference
             android:key="vibrate_on"
             android:title="@string/vibrate_on_keypress"
+            android:defaultValue="@bool/config_default_vibration_enabled"
             android:persistent="true" />
         <CheckBoxPreference
             android:key="sound_on"
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index 2dad171..80613a5 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -27,7 +27,7 @@
             />
 
     <ListPreference
-            android:key="pref_keyboard_layout_20100902"
+            android:key="pref_keyboard_layout_20110916"
             android:title="@string/keyboard_layout"
             android:persistent="true"
             android:entryValues="@array/keyboard_layout_modes_values"
diff --git a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
index a6304d8..2fb8b87 100644
--- a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
@@ -44,4 +44,8 @@
             return false;
         return (Boolean) CompatUtils.invoke(mVibrator, true, METHOD_hasVibrator);
     }
+
+    public void vibrate(long milliseconds) {
+        mVibrator.vibrate(milliseconds);
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index e43ae55..2d12369 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -20,6 +20,7 @@
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -48,7 +49,7 @@
     private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
     public static final boolean DEBUG_STATE = false;
 
-    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
+    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
     private static final int[] KEYBOARD_THEMES = {
         R.style.KeyboardTheme,
         R.style.KeyboardTheme_HighContrast,
@@ -97,6 +98,8 @@
     private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
     private int mSwitchState = SWITCH_STATE_ALPHA;
 
+    private static String mLayoutSwitchBackSymbols;
+
     private int mThemeIndex = -1;
     private Context mThemeContext;
 
@@ -204,6 +207,7 @@
             mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
             mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
             mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
+            mLayoutSwitchBackSymbols = mResources.getString(R.string.layout_switch_back_symbols);
             setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId()));
         } catch (RuntimeException e) {
             Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
@@ -661,24 +665,9 @@
         return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER;
     }
 
-    private static boolean isQuoteCharacter(int c) {
-        // Apostrophe, quotation mark.
-        if (c == Keyboard.CODE_SINGLE_QUOTE || c == Keyboard.CODE_DOUBLE_QUOTE)
-            return true;
-        // \u2018: Left single quotation mark
-        // \u2019: Right single quotation mark
-        // \u201a: Single low-9 quotation mark
-        // \u201b: Single high-reversed-9 quotation mark
-        // \u201c: Left double quotation mark
-        // \u201d: Right double quotation mark
-        // \u201e: Double low-9 quotation mark
-        // \u201f: Double high-reversed-9 quotation mark
-        if (c >= '\u2018' && c <= '\u201f')
-            return true;
-        // \u00ab: Left-pointing double angle quotation mark
-        // \u00bb: Right-pointing double angle quotation mark
-        if (c == '\u00ab' || c == '\u00bb')
-            return true;
+    private static boolean isLayoutSwitchBackCharacter(int c) {
+        if (TextUtils.isEmpty(mLayoutSwitchBackSymbols)) return false;
+        if (mLayoutSwitchBackSymbols.indexOf(c) >= 0) return true;
         return false;
     }
 
@@ -736,7 +725,7 @@
                 mSwitchState = SWITCH_STATE_SYMBOL;
             }
             // Snap back to alpha keyboard mode immediately if user types a quote character.
-            if (isQuoteCharacter(code)) {
+            if (isLayoutSwitchBackCharacter(code)) {
                 changeKeyboardMode();
             }
             break;
@@ -744,7 +733,7 @@
         case SWITCH_STATE_CHORDING_SYMBOL:
             // Snap back to alpha keyboard mode if user types one or more non-space/enter
             // characters followed by a space/enter or a quote character.
-            if (isSpaceCharacter(code) || isQuoteCharacter(code)) {
+            if (isSpaceCharacter(code) || isLayoutSwitchBackCharacter(code)) {
                 changeKeyboardMode();
             }
             break;
diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
index c4251cc..62a9259 100644
--- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
@@ -51,7 +51,7 @@
         mView = view;
         final String[] deviceList = context.getResources().getStringArray(
                 R.array.sudden_jumping_touch_event_device_list);
-        mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.DEVICE, deviceList);
+        mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.HARDWARE, deviceList);
     }
 
     private static boolean needsSuddenJumpingHack(String deviceName, String[] deviceList) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 61b2e93..91a7841 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -28,6 +28,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
+import android.os.Build;
 import android.os.Debug;
 import android.os.Message;
 import android.os.SystemClock;
@@ -56,6 +57,7 @@
 import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
 import com.android.inputmethod.compat.InputTypeCompatUtils;
 import com.android.inputmethod.compat.SuggestionSpanUtils;
+import com.android.inputmethod.compat.VibratorCompatWrapper;
 import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
 import com.android.inputmethod.deprecated.VoiceProxy;
 import com.android.inputmethod.deprecated.recorrection.Recorrection;
@@ -207,9 +209,12 @@
     private long mLastKeyTime;
 
     private AudioManager mAudioManager;
-    private static float mFxVolume = -1.0f; // just a default value to be updated runtime
+    private float mFxVolume = -1.0f; // default volume
     private boolean mSilentModeOn; // System-wide current configuration
 
+    private VibratorCompatWrapper mVibrator;
+    private long mKeypressVibrationDuration = -1;
+
     // TODO: Move this flag to VoiceProxy
     private boolean mConfigurationChanging;
 
@@ -433,13 +438,14 @@
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
         mRecorrection = Recorrection.getInstance();
+        mVibrator = VibratorCompatWrapper.getInstance(this);
         DEBUG = LatinImeLogger.sDBG;
 
-        loadSettings();
-
         final Resources res = getResources();
         mResources = res;
 
+        loadSettings();
+
         Utils.GCUtils.getInstance().reset();
         boolean tryGC = true;
         for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
@@ -480,6 +486,7 @@
         mSettingsValues = new Settings.Values(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
         resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
         updateSoundEffectVolume();
+        updateKeypressVibrationDuration();
     }
 
     private void initSuggest() {
@@ -2043,13 +2050,14 @@
 
     // update sound effect volume
     private void updateSoundEffectVolume() {
-        if (mAudioManager == null) {
-            mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-            if (mAudioManager == null) return;
+        final String[] volumePerHardwareList = mResources.getStringArray(R.array.keypress_volumes);
+        final String hardwarePrefix = Build.HARDWARE + ",";
+        for (final String element : volumePerHardwareList) {
+            if (element.startsWith(hardwarePrefix)) {
+                mFxVolume = Float.parseFloat(element.substring(element.lastIndexOf(',') + 1));
+                break;
+            }
         }
-        // This aligns with the current media volume minus 6dB
-        mFxVolume = (float) mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
-                / (float) mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) / 4.0f;
     }
 
     // update flags for silent mode
@@ -2061,6 +2069,19 @@
         mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
     }
 
+    private void updateKeypressVibrationDuration() {
+        final String[] durationPerHardwareList = mResources.getStringArray(
+                R.array.keypress_vibration_durations);
+        final String hardwarePrefix = Build.HARDWARE + ",";
+        for (final String element : durationPerHardwareList) {
+            if (element.startsWith(hardwarePrefix)) {
+                mKeypressVibrationDuration =
+                        Long.parseLong(element.substring(element.lastIndexOf(',') + 1));
+                break;
+            }
+        }
+    }
+
     private void playKeyClick(int primaryCode) {
         // if mAudioManager is null, we don't have the ringer state yet
         // mAudioManager will be set by updateRingerMode
@@ -2070,17 +2091,20 @@
             }
         }
         if (isSoundOn()) {
-            int sound = AudioManager.FX_KEYPRESS_STANDARD;
+            final int sound;
             switch (primaryCode) {
-                case Keyboard.CODE_DELETE:
-                    sound = AudioManager.FX_KEYPRESS_DELETE;
-                    break;
-                case Keyboard.CODE_ENTER:
-                    sound = AudioManager.FX_KEYPRESS_RETURN;
-                    break;
-                case Keyboard.CODE_SPACE:
-                    sound = AudioManager.FX_KEYPRESS_SPACEBAR;
-                    break;
+            case Keyboard.CODE_DELETE:
+                sound = AudioManager.FX_KEYPRESS_DELETE;
+                break;
+            case Keyboard.CODE_ENTER:
+                sound = AudioManager.FX_KEYPRESS_RETURN;
+                break;
+            case Keyboard.CODE_SPACE:
+                sound = AudioManager.FX_KEYPRESS_SPACEBAR;
+                break;
+            default:
+                sound = AudioManager.FX_KEYPRESS_STANDARD;
+                break;
             }
             mAudioManager.playSoundEffect(sound, mFxVolume);
         }
@@ -2090,11 +2114,16 @@
         if (!mSettingsValues.mVibrateOn) {
             return;
         }
-        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
-        if (inputView != null) {
-            inputView.performHapticFeedback(
-                    HapticFeedbackConstants.KEYBOARD_TAP,
-                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+        if (mKeypressVibrationDuration < 0) {
+            // Go ahead with the system default
+            LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+            if (inputView != null) {
+                inputView.performHapticFeedback(
+                        HapticFeedbackConstants.KEYBOARD_TAP,
+                        HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+            }
+        } else if (mVibrator != null) {
+            mVibrator.vibrate(mKeypressVibrationDuration);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
index 15a0cec..5a2eb16 100644
--- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
@@ -145,13 +145,6 @@
         // Nothing to do with.
     }
 
-    private final View.OnTouchListener mMotionEventDelegate = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View view, MotionEvent me) {
-            return MoreSuggestionsView.this.dispatchTouchEvent(me);
-        }
-    };
-
     @Override
     public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
             PopupWindow window, KeyboardActionListener listener) {
@@ -170,9 +163,7 @@
                 - (container.getMeasuredHeight() - container.getPaddingBottom())
                 + parentView.getPaddingTop() + mCoordinates[1];
 
-        container.setOnTouchListener(mMotionEventDelegate);
         window.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-        window.setFocusable(true);
         window.setOutsideTouchable(true);
         window.setContentView(container);
         window.setWidth(container.getMeasuredWidth());
@@ -193,6 +184,7 @@
 
     @Override
     public boolean dismissMoreKeysPanel() {
+        if (mController == null) return false;
         return mController.dismissMoreKeysPanel();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index c97f567..d706cd0 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -171,7 +171,8 @@
 
             // Get the settings preferences
             final boolean hasVibrator = VibratorCompatWrapper.getInstance(context).hasVibrator();
-            mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false);
+            mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON,
+                    res.getBoolean(R.bool.config_default_vibration_enabled));
             mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
                     res.getBoolean(R.bool.config_default_sound_enabled));
             mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res);
diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/SuggestionsView.java
index 13beb44..fbb2773 100644
--- a/java/src/com/android/inputmethod/latin/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/SuggestionsView.java
@@ -19,8 +19,14 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Message;
 import android.os.SystemClock;
@@ -167,6 +173,7 @@
         private final float mCenterSuggestionWeight;
         private final int mCenterSuggestionIndex;
         private final Drawable mMoreSuggestionsHint;
+        private static final String MORE_SUGGESTIONS_HINT = "\u2026";
 
         private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
         private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
@@ -225,7 +232,6 @@
             mCenterSuggestionWeight = getPercent(a,
                     R.styleable.SuggestionsView_centerSuggestionPercentile,
                     DEFAULT_CENTER_SUGGESTION_PERCENTILE);
-            mMoreSuggestionsHint = a.getDrawable(R.styleable.SuggestionsView_moreSuggestionsHint);
             mMaxMoreSuggestionsRow = a.getInt(
                     R.styleable.SuggestionsView_maxMoreSuggestionsRow,
                     DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
@@ -233,6 +239,8 @@
                     R.styleable.SuggestionsView_minMoreSuggestionsWidth);
             a.recycle();
 
+            mMoreSuggestionsHint = getMoreSuggestionsHint(res,
+                    res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect);
             mCenterSuggestionIndex = mSuggestionsCountInStrip / 2;
             mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
                     R.dimen.more_suggestions_bottom_gap);
@@ -246,6 +254,23 @@
             mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
         }
 
+        private static Drawable getMoreSuggestionsHint(Resources res, float textSize, int color) {
+            final Paint paint = new Paint();
+            paint.setAntiAlias(true);
+            paint.setTextAlign(Align.CENTER);
+            paint.setTextSize(textSize);
+            paint.setColor(color);
+            final Rect bounds = new Rect();
+            paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, 1, bounds);
+            final int width = Math.round(bounds.width() + 0.5f);
+            final int height = Math.round(bounds.height() + 0.5f);
+            final Bitmap buffer = Bitmap.createBitmap(
+                    width, (height * 3 / 2), Bitmap.Config.ARGB_8888);
+            final Canvas canvas = new Canvas(buffer);
+            canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint);
+            return new BitmapDrawable(res, buffer);
+        }
+
         // Read integer value in TypedArray as percent.
         private static float getPercent(TypedArray a, int index, int defValue) {
             return a.getInt(index, defValue) / 100.0f;
@@ -524,6 +549,21 @@
                 R.dimen.more_suggestions_modal_tolerance);
     }
 
+    private final View.OnTouchListener mMoreSuggestionsCanceller = new View.OnTouchListener() {
+        @Override
+        public boolean onTouch(View view, MotionEvent me) {
+            if (!mMoreSuggestionsWindow.isShowing()) return false;
+
+            switch (me.getAction()) {
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_POINTER_UP:
+                return mMoreSuggestionsView.dismissMoreKeysPanel();
+            default:
+                return true;
+            }
+        }
+    };
+
     /**
      * A connection back to the input method.
      * @param listener
@@ -739,6 +779,7 @@
         if (mMoreSuggestionsWindow.isShowing()) {
             mMoreSuggestionsWindow.dismiss();
             mKeyboardView.dimEntireKeyboard(false);
+            mKeyboardView.setOnTouchListener(null);
             return true;
         }
         return false;
@@ -770,18 +811,22 @@
             moreKeysPanel.showMoreKeysPanel(
                     this, mMoreSuggestionsController, pointX, pointY,
                     mMoreSuggestionsWindow, mMoreSuggestionsListener);
-            mCheckingIfModalOrSlidingMode = true;
+            mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
             mOriginX = mLastX;
             mOriginY = mLastY;
             view.setPressed(false);
             mKeyboardView.dimEntireKeyboard(true);
+            mKeyboardView.setOnTouchListener(mMoreSuggestionsCanceller);
             return true;
         }
         return false;
     }
 
     // Working variables for onLongClick and dispatchTouchEvent.
-    private boolean mCheckingIfModalOrSlidingMode;
+    private int mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
+    private static final int MORE_SUGGESTIONS_IN_MODAL_MODE = 0;
+    private static final int MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING = 1;
+    private static final int MORE_SUGGESTIONS_IN_SLIDING_MODE = 2;
     private int mLastX;
     private int mLastY;
     private int mOriginX;
@@ -790,7 +835,8 @@
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent me) {
-        if (!mMoreSuggestionsWindow.isShowing()) {
+        if (!mMoreSuggestionsWindow.isShowing()
+                || mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
             mLastX = (int)me.getX();
             mLastY = (int)me.getY();
             return super.dispatchTouchEvent(me);
@@ -807,22 +853,22 @@
         final int translatedX = moreKeysPanel.translateX(x);
         final int translatedY = moreKeysPanel.translateY(y);
 
-        if (mCheckingIfModalOrSlidingMode) {
+        if (mMoreSuggestionsMode == MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING) {
             if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
                     || mOriginY - y >= mMoreSuggestionsModalTolerance) {
                 // Decided to be in the sliding input mode only when the touch point has been moved
                 // upward.
-                mCheckingIfModalOrSlidingMode = false;
+                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_SLIDING_MODE;
                 tracker.onShowMoreKeysPanel(
                         translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel);
             } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
                 // Decided to be in the modal input mode
-                mCheckingIfModalOrSlidingMode = false;
+                mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE;
             }
             return true;
         }
 
-        // Process sliding motion events
+        // MORE_SUGGESTIONS_IN_SLIDING_MODE
         tracker.processMotionEvent(action, translatedX, translatedY, eventTime, moreKeysPanel);
         return true;
     }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 1e5b877..915c405 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -60,8 +60,11 @@
     private static final int CAPITALIZE_ALL = 2; // All caps
 
     private final static String[] EMPTY_STRING_ARRAY = new String[0];
-    private final static SuggestionsInfo EMPTY_SUGGESTIONS_INFO =
+    private final static SuggestionsInfo NOT_IN_DICT_EMPTY_SUGGESTIONS =
             new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
+    private final static SuggestionsInfo IN_DICT_EMPTY_SUGGESTIONS =
+            new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
+                    EMPTY_STRING_ARRAY);
     private Map<String, DictionaryPool> mDictionaryPools =
             Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
     private Map<String, Dictionary> mUserDictionaries =
@@ -330,7 +333,12 @@
             try {
                 final String text = textInfo.getText();
 
-                if (shouldFilterOut(text)) return EMPTY_SUGGESTIONS_INFO;
+                if (shouldFilterOut(text)) {
+                    final DictAndProximity dictInfo = mDictionaryPool.takeOrGetNull();
+                    if (null == dictInfo) return NOT_IN_DICT_EMPTY_SUGGESTIONS;
+                    return dictInfo.mDictionary.isValidWord(text) ? IN_DICT_EMPTY_SUGGESTIONS
+                            : NOT_IN_DICT_EMPTY_SUGGESTIONS;
+                }
 
                 final SuggestionsGatherer suggestionsGatherer =
                         new SuggestionsGatherer(suggestionsLimit);
@@ -353,23 +361,19 @@
 
                 final int capitalizeType = getCapitalizationType(text);
                 boolean isInDict = true;
-                try {
-                    final DictAndProximity dictInfo = mDictionaryPool.take();
-                    dictInfo.mDictionary.getWords(composer, suggestionsGatherer,
-                            dictInfo.mProximityInfo);
-                    isInDict = dictInfo.mDictionary.isValidWord(text);
-                    if (!isInDict && CAPITALIZE_NONE != capitalizeType) {
-                        // We want to test the word again if it's all caps or first caps only.
-                        // If it's fully down, we already tested it, if it's mixed case, we don't
-                        // want to test a lowercase version of it.
-                        isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
-                    }
-                    if (!mDictionaryPool.offer(dictInfo)) {
-                        Log.e(TAG, "Can't re-insert a dictionary into its pool");
-                    }
-                } catch (InterruptedException e) {
-                    // I don't think this can happen.
-                    return EMPTY_SUGGESTIONS_INFO;
+                final DictAndProximity dictInfo = mDictionaryPool.takeOrGetNull();
+                if (null == dictInfo) return NOT_IN_DICT_EMPTY_SUGGESTIONS;
+                dictInfo.mDictionary.getWords(composer, suggestionsGatherer,
+                        dictInfo.mProximityInfo);
+                isInDict = dictInfo.mDictionary.isValidWord(text);
+                if (!isInDict && CAPITALIZE_NONE != capitalizeType) {
+                    // We want to test the word again if it's all caps or first caps only.
+                    // If it's fully down, we already tested it, if it's mixed case, we don't
+                    // want to test a lowercase version of it.
+                    isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
+                }
+                if (!mDictionaryPool.offer(dictInfo)) {
+                    Log.e(TAG, "Can't re-insert a dictionary into its pool");
                 }
 
                 final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(text,
@@ -396,7 +400,7 @@
                     throw e;
                 } else {
                     Log.e(TAG, "Exception while spellcheking: " + e);
-                    return EMPTY_SUGGESTIONS_INFO;
+                    return NOT_IN_DICT_EMPTY_SUGGESTIONS;
                 }
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index ee294f6..dec18c1 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -56,6 +56,15 @@
         }
     }
 
+    // Convenience method
+    public DictAndProximity takeOrGetNull() {
+        try {
+            return take();
+        } catch (InterruptedException e) {
+            return null;
+        }
+    }
+
     public void close() {
         synchronized(this) {
             mClosed = true;
diff --git a/native/src/correction.h b/native/src/correction.h
index f3194b7..41130ad 100644
--- a/native/src/correction.h
+++ b/native/src/correction.h
@@ -119,8 +119,9 @@
     int mTerminalInputIndex;
     int mTerminalOutputIndex;
     unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
+    // Edit distance calculation requires a buffer with (N+1)^2 length for the input length N.
     // Caveat: Do not create multiple tables per thread as this table eats up RAM a lot.
-    int mEditDistanceTable[MAX_WORD_LENGTH_INTERNAL * MAX_WORD_LENGTH_INTERNAL];
+    int mEditDistanceTable[(MAX_WORD_LENGTH_INTERNAL + 1) * (MAX_WORD_LENGTH_INTERNAL + 1)];
 
     CorrectionState mCorrectionStates[MAX_WORD_LENGTH_INTERNAL];
 
diff --git a/native/src/defines.h b/native/src/defines.h
index 009d0ad..55469a7 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -131,7 +131,7 @@
 #endif // FLAG_DBG
 
 #ifndef U_SHORT_MAX
-#define U_SHORT_MAX 1 << 16
+#define U_SHORT_MAX 65535    // ((1 << 16) - 1)
 #endif
 #ifndef S_INT_MAX
 #define S_INT_MAX 2147483647 // ((1 << 31) - 1)