Merge "Don't show spacebar preview if key preview is disabled"
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 9b8d75e..fdfe282 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -128,6 +128,8 @@
     /** Height of keyboard */
     private int mKeyboardHeight;
 
+    private int mMostCommonKeyWidth = 0;
+
     public final KeyboardId mId;
 
     // Variables for pre-computing nearest keys.
@@ -411,21 +413,33 @@
      * @return The most common key width in the keyboard
      */
     public int getMostCommonKeyWidth() {
-        final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
-        int maxCount = 0;
-        int mostCommonWidth = 0;
-        for (final Key key : mKeys) {
-            final Integer width = key.mWidth + key.mGap;
-            Integer count = histogram.get(width);
-            if (count == null)
-                count = 0;
-            histogram.put(width, ++count);
-            if (count > maxCount) {
-                maxCount = count;
-                mostCommonWidth = width;
+        if (mMostCommonKeyWidth == 0) {
+            final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
+            int maxCount = 0;
+            int mostCommonWidth = 0;
+            for (final Key key : mKeys) {
+                final Integer width = key.mWidth + key.mGap;
+                Integer count = histogram.get(width);
+                if (count == null)
+                    count = 0;
+                histogram.put(width, ++count);
+                if (count > maxCount) {
+                    maxCount = count;
+                    mostCommonWidth = width;
+                }
             }
+            mMostCommonKeyWidth = mostCommonWidth;
         }
-        return mostCommonWidth;
+        return mMostCommonKeyWidth;
+    }
+
+    /**
+     * Return true if spacebar needs showing preview even when "popup on keypress" is off.
+     * @param keyIndex index of the pressing key
+     * @return true if spacebar needs showing preview
+     */
+    public boolean needSpacebarPreview(int keyIndex) {
+        return false;
     }
 
     private void loadKeyboard(Context context, int xmlLayoutResId) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index a140a97..3550dcb 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -18,7 +18,6 @@
 
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -224,7 +223,7 @@
 
         public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
             removeMessages(MSG_POPUP_PREVIEW);
-            if (mPreviewText.getVisibility() == VISIBLE) {
+            if (mPreviewText.getVisibility() == VISIBLE || delay == 0) {
                 // Show right away, if it's already visible and finger is moving around
                 showKey(keyIndex, tracker);
             } else {
@@ -889,15 +888,7 @@
     public void showPreview(int keyIndex, PointerTracker tracker) {
         int oldKeyIndex = mOldPreviewKeyIndex;
         mOldPreviewKeyIndex = keyIndex;
-        // We should re-draw popup preview when 1) we need to hide the preview, 2) we will show
-        // the space key preview and 3) pointer moves off the space key to other letter key, we
-        // should hide the preview of the previous key.
-        final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null)
-                || (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher()
-                        && SubtypeSwitcher.getInstance().needsToDisplayLanguage()
-                        && (tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex)));
-        // If key changed and preview is on or the key is space (language switch is enabled)
-        if (oldKeyIndex != keyIndex && (mShowPreview || (hidePreviewOrShowSpaceKeyPreview))) {
+        if ((mShowPreview && oldKeyIndex != keyIndex) || mKeyboard.needSpacebarPreview(keyIndex)) {
             if (keyIndex == KeyDetector.NOT_A_KEY) {
                 mHandler.cancelPopupPreview();
                 mHandler.dismissPreview(mDelayAfterPreview);
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 9d9793e..3a5dbd2 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -26,6 +26,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
@@ -33,15 +36,18 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 
+import java.lang.ref.SoftReference;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 
 // TODO: We should remove this class
 public class LatinKeyboard extends Keyboard {
-    public static final int OPACITY_FULLY_OPAQUE = 255;
     private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
     private final Context mContext;
+    private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
 
     /* Space key and its icons, drawables and colors. */
     private final Key mSpaceKey;
@@ -59,14 +65,18 @@
     private int mSpaceDragLastDiff;
     private boolean mCurrentlyInSpace;
     private SlidingLocaleDrawable mSlidingLocaleIcon;
+    private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache =
+            new HashMap<Integer, SoftReference<BitmapDrawable>>();
 
     /* Shortcut key and its icons if available */
     private final Key mShortcutKey;
     private final Drawable mEnabledShortcutIcon;
     private final Drawable mDisabledShortcutIcon;
 
-    private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f;
-    // Minimum width of space key preview (proportional to keyboard width)
+    // Minimum width of spacebar dragging to trigger the language switch (represented by the number
+    // of the most common key width of this keyboard).
+    private static final int SPACEBAR_DRAG_WIDTH = 3;
+    // Minimum width of space key preview (proportional to keyboard width).
     private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f;
     // Height in space key the language name will be drawn. (proportional to space key height)
     public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
@@ -137,6 +147,12 @@
         return newColor;
     }
 
+    private static ColorFilter getSpacebarDrawableFilter(float fadeFactor) {
+        final ColorMatrix colorMatrix = new ColorMatrix();
+        colorMatrix.setScale(1, 1, 1, fadeFactor);
+        return new ColorMatrixColorFilter(colorMatrix);
+    }
+
     public void updateShortcutKey(boolean available, LatinKeyboardView view) {
         if (mShortcutKey == null)
             return;
@@ -157,19 +173,14 @@
     private void updateSpacebarForLocale(boolean isAutoCorrection) {
         if (mSpaceKey == null)
             return;
-        final Resources res = mContext.getResources();
         // If application locales are explicitly selected.
-        if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) {
-            mSpaceKey.setIcon(new BitmapDrawable(res,
-                    drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection)));
+        if (mSubtypeSwitcher.needsToDisplayLanguage()) {
+            mSpaceKey.setIcon(getSpaceDrawable(
+                    mSubtypeSwitcher.getInputLocale(), isAutoCorrection));
+        } else if (isAutoCorrection) {
+            mSpaceKey.setIcon(getSpaceDrawable(null, true));
         } else {
-            // sym_keyboard_space_led can be shared with Black and White symbol themes.
-            if (isAutoCorrection) {
-                mSpaceKey.setIcon(new BitmapDrawable(res,
-                        drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection)));
-            } else {
-                mSpaceKey.setIcon(mSpaceIcon);
-            }
+            mSpaceKey.setIcon(mSpaceIcon);
         }
     }
 
@@ -223,19 +234,31 @@
         return language;
     }
 
-    private Bitmap drawSpacebar(int opacity, boolean isAutoCorrection) {
+    private BitmapDrawable getSpaceDrawable(Locale locale, boolean isAutoCorrection) {
+        final Integer hashCode = Arrays.hashCode(
+                new Object[] { locale, isAutoCorrection, mSpacebarTextFadeFactor });
+        final SoftReference<BitmapDrawable> ref = mSpaceDrawableCache.get(hashCode);
+        BitmapDrawable drawable = (ref == null) ? null : ref.get();
+        if (drawable == null) {
+            drawable = new BitmapDrawable(mContext.getResources(), drawSpacebar(
+                    locale, isAutoCorrection, mSpacebarTextFadeFactor));
+            mSpaceDrawableCache.put(hashCode, new SoftReference<BitmapDrawable>(drawable));
+        }
+        return drawable;
+    }
+
+    private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection,
+            float textFadeFactor) {
         final int width = mSpaceKey.mWidth;
         final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight;
         final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         final Canvas canvas = new Canvas(buffer);
         final Resources res = mContext.getResources();
-        canvas.drawColor(res.getColor(android.R.color.transparent), PorterDuff.Mode.CLEAR);
+        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
 
-        SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
         // If application locales are explicitly selected.
-        if (subtypeSwitcher.needsToDisplayLanguage()) {
+        if (inputLocale != null) {
             final Paint paint = new Paint();
-            paint.setAlpha(opacity);
             paint.setAntiAlias(true);
             paint.setTextAlign(Align.CENTER);
 
@@ -253,7 +276,7 @@
             }
 
             final boolean allowVariableTextSize = true;
-            final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(),
+            final String language = layoutSpacebar(paint, inputLocale,
                     mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height,
                     getTextSizeFromTheme(mContext.getTheme(), textStyle, defaultTextSize),
                     allowVariableTextSize);
@@ -265,14 +288,16 @@
             final float textHeight = -paint.ascent() + descent;
             final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE
                     : height / 2 + textHeight / 2;
-            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent - 1, paint);
-            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor));
+            paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor));
             canvas.drawText(language, width / 2, baseline - descent, paint);
 
             // Put arrows that are already layed out on either side of the text
-            if (subtypeSwitcher.useSpacebarLanguageSwitcher()
-                    && subtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
+            if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()
+                    && mSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
+                mButtonArrowLeftIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
+                mButtonArrowRightIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor));
                 mButtonArrowLeftIcon.draw(canvas);
                 mButtonArrowRightIcon.draw(canvas);
             }
@@ -316,9 +341,25 @@
         mSpaceKey.getPreviewIcon().invalidateSelf();
     }
 
+    // This method is called when "popup on keypress" is off.
+    @Override
+    public boolean needSpacebarPreview(int keyIndex) {
+        if (!mSubtypeSwitcher.useSpacebarLanguageSwitcher())
+            return false;
+        // Dismiss key preview.
+        if (keyIndex == KeyDetector.NOT_A_KEY)
+            return true;
+        // Key is not a spacebar.
+        if (keyIndex != mSpaceKeyIndexArray[0])
+            return false;
+        // The language switcher will be displayed only when the dragging distance is greater
+        // than average key width of this keyboard.
+        return Math.abs(mSpaceDragLastDiff) > getMostCommonKeyWidth();
+    }
+
     public int getLanguageChangeDirection() {
         if (mSpaceKey == null || SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() <= 1
-                || Math.abs(mSpaceDragLastDiff) < mSpaceKey.mWidth * SPACEBAR_DRAG_THRESHOLD) {
+                || Math.abs(mSpaceDragLastDiff) < getMostCommonKeyWidth() * SPACEBAR_DRAG_WIDTH) {
             return 0; // No change
         }
         return mSpaceDragLastDiff > 0 ? 1 : -1;
diff --git a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
index eee0ac6..b279c1c 100644
--- a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
+++ b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java
@@ -23,6 +23,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -63,9 +64,8 @@
         mHeight = height;
         final TextPaint textPaint = new TextPaint();
         textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18));
-        textPaint.setColor(android.R.color.transparent);
+        textPaint.setColor(Color.TRANSPARENT);
         textPaint.setTextAlign(Align.CENTER);
-        textPaint.setAlpha(LatinKeyboard.OPACITY_FULLY_OPAQUE);
         textPaint.setAntiAlias(true);
         mTextPaint = textPaint;
         mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2;