Merge "Improve space substitution error correction."
diff --git a/common/src/com/android/inputmethod/latin/common/Constants.java b/common/src/com/android/inputmethod/latin/common/Constants.java
index a0cbd2c..d374642 100644
--- a/common/src/com/android/inputmethod/latin/common/Constants.java
+++ b/common/src/com/android/inputmethod/latin/common/Constants.java
@@ -296,8 +296,6 @@
         return "[" + sb + "]";
     }
 
-    public static final int MAX_INT_BIT_COUNT = 32;
-
     /**
      * Screen metrics (a.k.a. Device form factor) constants of
      * {@link com.android.inputmethod.latin.R.integer#config_screen_metrics}.
@@ -307,6 +305,16 @@
     public static final int SCREEN_METRICS_LARGE_TABLET = 2;
     public static final int SCREEN_METRICS_SMALL_TABLET = 3;
 
+    public static boolean isPhone(final int screenMetrics) {
+        return screenMetrics == SCREEN_METRICS_SMALL_PHONE
+                || screenMetrics == SCREEN_METRICS_LARGE_PHONE;
+    }
+
+    public static boolean isTablet(final int screenMetrics) {
+        return screenMetrics == SCREEN_METRICS_SMALL_TABLET
+                || screenMetrics == SCREEN_METRICS_LARGE_TABLET;
+    }
+
     /**
      * Default capacity of gesture points container.
      * This constant is used by {@link com.android.inputmethod.keyboard.internal.BatchInputArbiter}
diff --git a/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java b/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java
deleted file mode 100644
index 27b2f50..0000000
--- a/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2014, 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.view.KeyEvent;
-
-final class SpecialKeyDetector {
-    /**
-     * Special physical key detector
-     * @param context a context of this detector.
-     */
-    public SpecialKeyDetector(final Context context) {
-    }
-
-    /**
-     * Record a down key event.
-     * @param keyEvent a down key event.
-     */
-    public void onKeyDown(final KeyEvent keyEvent) {
-    }
-
-    /**
-     * Record an up key event.
-     * @param keyEvent an up key event.
-     */
-    public void onKeyUp(final KeyEvent keyEvent) {
-    }
-}
diff --git a/java/res/values/donottranslate-debug-settings.xml b/java/res/values/donottranslate-debug-settings.xml
index cc8c1a0..c612010 100644
--- a/java/res/values/donottranslate-debug-settings.xml
+++ b/java/res/values/donottranslate-debug-settings.xml
@@ -22,7 +22,6 @@
     <string name="english_ime_debug_settings">Android Keyboard Debug settings</string>
     <string name="prefs_debug_mode">Debug Mode</string>
     <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string>
-    <string name="prefs_force_physical_keyboard_special_key">Force physical keyboard special key</string>
     <string name="prefs_should_show_lxx_suggestion_ui">Show LXX suggestion UI</string>
     <!-- Option to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=30]-->
     <string name="sliding_key_input_preview">Show slide indicator</string>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 54bfc51..583c3b1 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -363,6 +363,12 @@
     <string name="prefs_keypress_sound_volume_settings">Keypress sound volume</string>
     <!-- Title of the settings for key long press delay [CHAR LIMIT=35] -->
     <string name="prefs_key_longpress_timeout_settings">Key long press delay</string>
+    <!-- TODO: Let's finalize title of the settings and remove translatable="false" -->
+    <!-- Title of the settings for enabling Emoji palette triggered by the Alt key on physical keyboards [CHAR LIMIT=35] -->
+    <string name="prefs_enable_emoji_alt_physical_key" translatable="false">Emoji for physical keyboard</string>
+    <!-- TODO: Let's finalize title of the settings and remove translatable="false" -->
+    <!-- Description of the settings for enabling Emoji palette triggered by the Alt key on physical keyboards [CHAR LIMIT=64] -->
+    <string name="prefs_enable_emoji_alt_physical_key_summary" translatable="false">Physical Alt key shows the emoji palette</string>
 
     <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] -->
     <string name="button_default">Default</string>
diff --git a/java/res/xml/prefs_screen_advanced.xml b/java/res/xml/prefs_screen_advanced.xml
index 3298220..1fa6fd0 100644
--- a/java/res/xml/prefs_screen_advanced.xml
+++ b/java/res/xml/prefs_screen_advanced.xml
@@ -37,6 +37,12 @@
         latin:minValue="@integer/config_min_longpress_timeout"
         latin:maxValue="@integer/config_max_longpress_timeout"
         latin:stepValue="@integer/config_longpress_timeout_step" />
+    <CheckBoxPreference
+        android:key="pref_enable_emoji_alt_physical_key"
+        android:title="@string/prefs_enable_emoji_alt_physical_key"
+        android:summary="@string/prefs_enable_emoji_alt_physical_key_summary"
+        android:defaultValue="true"
+        android:persistent="true" />
     <!-- The settings for showing setup wizard application icon shouldn't be persistent and
          the default value is added programmatically. -->
     <CheckBoxPreference
diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml
index 486d236..13edf3e 100644
--- a/java/res/xml/prefs_screen_debug.xml
+++ b/java/res/xml/prefs_screen_debug.xml
@@ -31,11 +31,6 @@
         android:defaultValue="false"
         android:persistent="true" />
     <CheckBoxPreference
-        android:key="force_physical_keyboard_special_key"
-        android:title="@string/prefs_force_physical_keyboard_special_key"
-        android:defaultValue="false"
-        android:persistent="true" />
-    <CheckBoxPreference
         android:key="pref_should_show_lxx_suggestion_ui"
         android:title="@string/prefs_should_show_lxx_suggestion_ui"
         android:defaultValue="true"
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java
index 0e8ce7f..a9711ae 100644
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java
+++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java
@@ -30,7 +30,6 @@
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.settings.Settings;
 
 import java.util.ArrayList;
@@ -271,7 +270,7 @@
     }
 
     private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) {
-        return (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id;
+        return (((long) categoryId) << Integer.SIZE) | id;
     }
 
     public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
diff --git a/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java
new file mode 100644
index 0000000..8116a49
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/EmojiAltPhysicalKeyDetector.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014, 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.view.KeyEvent;
+
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.latin.settings.Settings;
+
+/**
+ * A class for detecting Emoji-Alt physical key.
+ */
+final class EmojiAltPhysicalKeyDetector {
+    // True if the Alt key has been used as a modifier. In this case the Alt key up isn't
+    // recognized as an emoji key.
+    private boolean mAltHasBeenUsedAsAModifier;
+
+    /**
+     * Record a down key event.
+     * @param keyEvent a down key event.
+     */
+    public void onKeyDown(final KeyEvent keyEvent) {
+        if (isAltKey(keyEvent)) {
+            mAltHasBeenUsedAsAModifier = false;
+        }
+        if (containsAltModifier(keyEvent)) {
+            mAltHasBeenUsedAsAModifier = true;
+        }
+    }
+
+    /**
+     * Determine whether an up key event is a special key up or not.
+     * @param keyEvent an up key event.
+     */
+    public void onKeyUp(final KeyEvent keyEvent) {
+        if (keyEvent.isCanceled()) {
+            // This key up event was a part of key combinations and should be ignored.
+            return;
+        }
+        if (!isAltKey(keyEvent)) {
+            mAltHasBeenUsedAsAModifier |= containsAltModifier(keyEvent);
+            return;
+        }
+        if (containsAltModifier(keyEvent)) {
+            mAltHasBeenUsedAsAModifier = true;
+            return;
+        }
+        if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) {
+            return;
+        }
+        if (!mAltHasBeenUsedAsAModifier) {
+            onEmojiAltKeyDetected();
+        }
+    }
+
+    private static void onEmojiAltKeyDetected() {
+        KeyboardSwitcher.getInstance().onToggleEmojiKeyboard();
+    }
+
+    private static boolean isAltKey(final KeyEvent keyEvent) {
+        final int keyCode = keyEvent.getKeyCode();
+        return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT;
+    }
+
+    private static boolean containsAltModifier(final KeyEvent keyEvent) {
+        final int metaState = keyEvent.getMetaState();
+        // TODO: Support multiple keyboards. Take device id into account.
+        switch (keyEvent.getKeyCode()) {
+        case KeyEvent.KEYCODE_ALT_LEFT:
+            // Return true if Left-Alt is pressed with Right-Alt pressed.
+            return (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0;
+        case KeyEvent.KEYCODE_ALT_RIGHT:
+            // Return true if Right-Alt is pressed with Left-Alt pressed.
+            return (metaState & KeyEvent.META_ALT_LEFT_ON) != 0;
+        default:
+            return (metaState & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON)) != 0;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 11cbec3..ff82087 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -167,7 +167,8 @@
     @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
     final SubtypeSwitcher mSubtypeSwitcher;
     private final SubtypeState mSubtypeState = new SubtypeState();
-    private final SpecialKeyDetector mSpecialKeyDetector;
+    private final EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector =
+            new EmojiAltPhysicalKeyDetector();
     private StatsUtilsManager mStatsUtilsManager;
     // Working variable for {@link #startShowingInputView()} and
     // {@link #onEvaluateInputViewShown()}.
@@ -545,7 +546,6 @@
         mSettings = Settings.getInstance();
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
-        mSpecialKeyDetector = new SpecialKeyDetector(this);
         mStatsUtilsManager = StatsUtilsManager.getInstance();
         mIsHardwareAcceleratedDrawingEnabled =
                 InputMethodServiceCompatUtils.enableHardwareAcceleration(this);
@@ -1765,7 +1765,8 @@
     // Hooks for hardware keyboard
     @Override
     public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
-        mSpecialKeyDetector.onKeyDown(keyEvent);
+        // TODO: This should be processed in {@link InputLogic}.
+        mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent);
         if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
             return super.onKeyDown(keyCode, keyEvent);
         }
@@ -1786,7 +1787,8 @@
 
     @Override
     public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) {
-        mSpecialKeyDetector.onKeyUp(keyEvent);
+        // TODO: This should be processed in {@link InputLogic}.
+        mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent);
         if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
             return super.onKeyUp(keyCode, keyEvent);
         }
diff --git a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java
index a9884ba..554edc8 100644
--- a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java
@@ -19,9 +19,9 @@
 import android.os.Bundle;
 
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.define.ProductionFlags;
 
-
 /**
  * "Appearance" settings sub screen.
  */
@@ -30,8 +30,8 @@
     public void onCreate(final Bundle icicle) {
         super.onCreate(icicle);
         addPreferencesFromResource(R.xml.prefs_screen_appearance);
-        if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED
-                || !Settings.getInstance().getCurrent().isTablet()) {
+        if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED ||
+                Constants.isPhone(Settings.readScreenMetrics(getResources()))) {
             removePreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD);
         }
     }
@@ -43,4 +43,4 @@
                 findPreference(Settings.PREF_CUSTOM_INPUT_STYLES));
         ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME));
     }
-}
\ No newline at end of file
+}
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index 768cba9..4985c2f 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -27,8 +27,6 @@
 public final class DebugSettings {
     public static final String PREF_DEBUG_MODE = "debug_mode";
     public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch";
-    public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY =
-            "force_physical_keyboard_special_key";
     public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS =
             "pref_has_custom_key_preview_animation_params";
     public static final String PREF_KEY_PREVIEW_DISMISS_DURATION =
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
index 475f1de..2e5c3c4 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
@@ -142,8 +142,7 @@
             mServiceNeedsRestart = true;
             return;
         }
-        if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH)
-                || key.equals(DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY)) {
+        if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH)) {
             mServiceNeedsRestart = true;
             return;
         }
diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
index c9e9dc8..0fd94b0 100644
--- a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
+++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
@@ -46,7 +46,6 @@
         // correctly set for it to work on a new device.
         DebugSettings.PREF_DEBUG_MODE,
         DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH,
-        DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY,
         DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
         DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 5e23693..6d23fcb 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -97,6 +97,8 @@
             "pref_vibration_duration_settings";
     public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume";
     public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
+    public static final String PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY =
+            "pref_enable_emoji_alt_physical_key";
     public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
     public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
             "pref_gesture_floating_preview_text";
@@ -211,6 +213,10 @@
         return mSettingsValues.mBlockPotentiallyOffensive;
     }
 
+    public static int readScreenMetrics(final Resources res) {
+        return res.getInteger(R.integer.config_screen_metrics);
+    }
+
     // Accessed from the settings interface, hence public
     public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
             final Resources res) {
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 660be06..bdb4e64 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -29,7 +29,6 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.SubtypeSwitcher;
-import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
@@ -80,6 +79,7 @@
     public final boolean mSlidingKeyInputPreviewEnabled;
     public final boolean mPhraseGestureEnabled;
     public final int mKeyLongpressTimeout;
+    public final boolean mEnableEmojiAltPhysicalKey;
     public final boolean mEnableMetricsLogging;
     public final boolean mShouldShowLxxSuggestionUi;
     // Use split layout for keyboard.
@@ -157,7 +157,7 @@
         mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
         mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
         mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false);
-        mScreenMetrics = res.getInteger(R.integer.config_screen_metrics);
+        mScreenMetrics = Settings.readScreenMetrics(res);
 
         mShouldShowLxxSuggestionUi = Settings.SHOULD_SHOW_LXX_SUGGESTION_UI
                 && prefs.getBoolean(DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, true);
@@ -166,6 +166,8 @@
         mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
         mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res);
         mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res);
+        mEnableEmojiAltPhysicalKey = prefs.getBoolean(
+                Settings.PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY, true);
         mAutoCorrectionThreshold = readAutoCorrectionThreshold(res,
                 autoCorrectionThresholdRawValue);
         mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res);
@@ -221,11 +223,6 @@
         return mEnableMetricsLogging;
     }
 
-    public boolean isTablet() {
-        return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET
-                || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET;
-    }
-
     public boolean isApplicationSpecifiedCompletionsOn() {
         return mInputAttributes.mApplicationSpecifiedCompletionOn;
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index d0df724..f9839eb 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -19,15 +19,17 @@
 import java.util.ArrayList;
 import java.util.Collection;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 public final class CollectionUtils {
     private CollectionUtils() {
         // This utility class is not publicly instantiable.
     }
 
-    public static <E> ArrayList<E> arrayAsList(final E[] array, final int start, final int end) {
-        if (array == null) {
-            throw new NullPointerException();
-        }
+    @Nonnull
+    public static <E> ArrayList<E> arrayAsList(@Nonnull final E[] array, final int start,
+            final int end) {
         if (start < 0 || start > end || end > array.length) {
             throw new IllegalArgumentException();
         }
@@ -44,7 +46,7 @@
      * @param c Collection to test.
      * @return Whether c contains no elements.
      */
-    public static boolean isNullOrEmpty(final Collection<?> c) {
+    public static boolean isNullOrEmpty(@Nullable final Collection<?> c) {
         return c == null || c.isEmpty();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java b/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java
index 87df013..3a97059 100644
--- a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.latin.utils;
 
-import java.util.Arrays;
+import javax.annotation.Nonnull;
 
 public final class CoordinateUtils {
     private static final int INDEX_X = 0;
@@ -27,32 +27,35 @@
         // This utility class is not publicly instantiable.
     }
 
+    @Nonnull
     public static int[] newInstance() {
         return new int[ELEMENT_SIZE];
     }
 
-    public static int x(final int[] coords) {
+    public static int x(@Nonnull final int[] coords) {
         return coords[INDEX_X];
     }
 
-    public static int y(final int[] coords) {
+    public static int y(@Nonnull final int[] coords) {
         return coords[INDEX_Y];
     }
 
-    public static void set(final int[] coords, final int x, final int y) {
+    public static void set(@Nonnull final int[] coords, final int x, final int y) {
         coords[INDEX_X] = x;
         coords[INDEX_Y] = y;
     }
 
-    public static void copy(final int[] destination, final int[] source) {
+    public static void copy(@Nonnull final int[] destination, @Nonnull final int[] source) {
         destination[INDEX_X] = source[INDEX_X];
         destination[INDEX_Y] = source[INDEX_Y];
     }
 
+    @Nonnull
     public static int[] newCoordinateArray(final int arraySize) {
         return new int[ELEMENT_SIZE * arraySize];
     }
 
+    @Nonnull
     public static int[] newCoordinateArray(final int arraySize,
             final int defaultX, final int defaultY) {
         final int[] result = new int[ELEMENT_SIZE * arraySize];
@@ -62,30 +65,30 @@
         return result;
     }
 
-    public static int xFromArray(final int[] coordsArray, final int index) {
+    public static int xFromArray(@Nonnull final int[] coordsArray, final int index) {
         return coordsArray[ELEMENT_SIZE * index + INDEX_X];
     }
 
-    public static int yFromArray(final int[] coordsArray, final int index) {
+    public static int yFromArray(@Nonnull final int[] coordsArray, final int index) {
         return coordsArray[ELEMENT_SIZE * index + INDEX_Y];
     }
 
-    public static int[] coordinateFromArray(final int[] coordsArray, final int index) {
-        final int baseIndex = ELEMENT_SIZE * index;
-        return Arrays.copyOfRange(coordsArray, baseIndex, baseIndex + ELEMENT_SIZE);
+    @Nonnull
+    public static int[] coordinateFromArray(@Nonnull final int[] coordsArray, final int index) {
+        final int[] coords = newInstance();
+        set(coords, xFromArray(coordsArray, index), yFromArray(coordsArray, index));
+        return coords;
     }
 
-    public static void setXYInArray(final int[] coordsArray, final int index,
+    public static void setXYInArray(@Nonnull final int[] coordsArray, final int index,
             final int x, final int y) {
         final int baseIndex = ELEMENT_SIZE * index;
         coordsArray[baseIndex + INDEX_X] = x;
         coordsArray[baseIndex + INDEX_Y] = y;
     }
 
-    public static void setCoordinateInArray(final int[] coordsArray, final int index,
-            final int[] coords) {
-        final int baseIndex = ELEMENT_SIZE * index;
-        coordsArray[baseIndex + INDEX_X] = coords[INDEX_X];
-        coordsArray[baseIndex + INDEX_Y] = coords[INDEX_Y];
+    public static void setCoordinateInArray(@Nonnull final int[] coordsArray, final int index,
+            @Nonnull final int[] coords) {
+        setXYInArray(coordsArray, index, x(coords), y(coords));
     }
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
index 249d822..d3de322 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
@@ -97,6 +97,9 @@
         return NOT_A_WORD_ID;
     }
     const PtNodeParams ptNodeParams = mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos);
+    if (ptNodeParams.isDeleted()) {
+        return NOT_A_WORD_ID;
+    }
     return ptNodeParams.getTerminalId();
 }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
index ec836cc..2361968 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -87,7 +87,7 @@
         setContext(new ContextThemeWrapper(getContext(), keyboardTheme.mStyleId));
         KeyboardLayoutSet.onKeyboardThemeChanged();
 
-        mScreenMetrics = res.getInteger(R.integer.config_screen_metrics);
+        mScreenMetrics = Settings.readScreenMetrics(res);
         RichInputMethodManager.init(context);
         final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
 
@@ -121,8 +121,7 @@
     }
 
     protected final boolean isPhone() {
-        return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE
-                || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
+        return Constants.isPhone(mScreenMetrics);
     }
 
     protected final InputMethodSubtype getSubtype(final Locale locale,
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index 47badc1..f90b266 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -746,7 +746,12 @@
         assertTrue(binaryDictionary.isValidWord("aaa"));
         assertTrue(binaryDictionary.removeUnigramEntry("aaa"));
         assertFalse(binaryDictionary.isValidWord("aaa"));
-
+        onInputWord(binaryDictionary, "aaa", false /* isValidWord */);
+        assertFalse(binaryDictionary.isValidWord("aaa"));
+        onInputWord(binaryDictionary, "aaa", false /* isValidWord */);
+        assertTrue(binaryDictionary.isValidWord("aaa"));
+        assertTrue(binaryDictionary.removeUnigramEntry("aaa"));
+        assertFalse(binaryDictionary.isValidWord("aaa"));
         binaryDictionary.close();
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 7d356a4..c76f6f4 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -674,10 +674,15 @@
                 mEditText.getText().toString());
     }
 
-    private void typeWordAndPutCursorInside(final String word, final int startPos) {
+    private void typeOrGestureWordAndPutCursorInside(final boolean gesture, final String word,
+            final int startPos) {
         final int END_OF_WORD = startPos + word.length();
         final int NEW_CURSOR_POSITION = startPos + word.length() / 2;
-        type(word);
+        if (gesture) {
+            gesture(word);
+        } else {
+            type(word);
+        }
         sendUpdateForCursorMoveTo(END_OF_WORD);
         runMessages();
         sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION);
@@ -687,6 +692,14 @@
                 startPos, END_OF_WORD);
     }
 
+    private void typeWordAndPutCursorInside(final String word, final int startPos) {
+        typeOrGestureWordAndPutCursorInside(false /* gesture */, word, startPos);
+    }
+
+    private void gestureWordAndPutCursorInside(final String word, final int startPos) {
+        typeOrGestureWordAndPutCursorInside(true /* gesture */, word, startPos);
+    }
+
     private void ensureComposingSpanPos(final String message, final int from, final int to) {
         assertEquals(message, from, BaseInputConnection.getComposingSpanStart(mEditText.getText()));
         assertEquals(message, to, BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
@@ -703,6 +716,23 @@
         int cursorPos = sendUpdateForCursorMoveToEndOfLine();
         runMessages();
         type(" ");
+        assertEquals("mbo", "some thing ", mEditText.getText().toString());
+        typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */);
+        type(Constants.CODE_DELETE);
+        ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1);
+    }
+
+    public void testTypeWithinGestureComposing() {
+        final String WORD_TO_TYPE = "something";
+        final String EXPECTED_RESULT = "some thing";
+        gestureWordAndPutCursorInside(WORD_TO_TYPE, 0 /* startPos */);
+        type(" ");
+        ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1);
+        assertEquals("space in the middle of a composing word", EXPECTED_RESULT,
+                mEditText.getText().toString());
+        int cursorPos = sendUpdateForCursorMoveToEndOfLine();
+        runMessages();
+        type(" ");
         typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */);
         type(Constants.CODE_DELETE);
         ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1);
diff --git a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
index f44673e..ed632db 100644
--- a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
+++ b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
@@ -20,7 +20,6 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.utils.RunInLocale;
@@ -37,13 +36,11 @@
     private int mScreenMetrics;
 
     private boolean isPhone() {
-        return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE
-                || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
+        return Constants.isPhone(mScreenMetrics);
     }
 
     private boolean isTablet() {
-        return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET
-                || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET;
+        return Constants.isTablet(mScreenMetrics);
     }
 
     private SpacingAndPunctuations ENGLISH;
@@ -70,7 +67,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics);
+        mScreenMetrics = Settings.readScreenMetrics(getContext().getResources());
 
         // Language only
         ENGLISH = getSpacingAndPunctuations(Locale.ENGLISH);
diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
index 3feb60e..a5979c3 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
@@ -51,5 +51,4 @@
         assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_SET));
         assertFalse(CollectionUtils.isNullOrEmpty(Collections.singleton("Not empty")));
     }
-
 }