Make sure to set symbol keyboard shifted

To avoid a corner case of bug#3070963, in toggleShift() method of
KeyboardSwitcher, the shifted symbol keyboard will be set if current
keyboard is symbol keyboard or is not shifted symbol keyboard.

This change also implements mini keyboard cache with WeakHashMap.

Bug: 3070963
Change-Id: I868fc072e2f21bddded1622b800a53b9a6a43e91
diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
index ebf2f4e..86dbf1f 100644
--- a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -23,9 +23,9 @@
 import android.view.InflateException;
 
 import java.lang.ref.SoftReference;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Locale;
-import java.util.Map;
 
 public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
 
@@ -105,7 +105,7 @@
     private KeyboardId mSymbolsShiftedId;
 
     private KeyboardId mCurrentId;
-    private final Map<KeyboardId, SoftReference<LatinKeyboard>> mKeyboards;
+    private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboards;
 
     private int mMode = MODE_NONE; /** One of the MODE_XXX values */
     private int mImeOptions;
@@ -194,11 +194,17 @@
         public final boolean mEnableShiftLock;
         public final boolean mHasVoice;
 
+        private final int mHashCode;
+
         public KeyboardId(int xml, int mode, boolean enableShiftLock, boolean hasVoice) {
             this.mXml = xml;
             this.mKeyboardMode = mode;
             this.mEnableShiftLock = enableShiftLock;
             this.mHasVoice = hasVoice;
+
+            this.mHashCode = Arrays.hashCode(new Object[] {
+               xml, mode, enableShiftLock, hasVoice
+            });
         }
 
         public KeyboardId(int xml, boolean hasVoice) {
@@ -219,8 +225,7 @@
 
         @Override
         public int hashCode() {
-            return (mXml + 1) * (mKeyboardMode + 1) * (mEnableShiftLock ? 2 : 1)
-                    * (mHasVoice ? 4 : 8);
+            return mHashCode;
         }
     }
 
@@ -378,7 +383,9 @@
     }
 
     public void toggleShift() {
-        if (mCurrentId.equals(mSymbolsId)) {
+        if (isAlphabetMode())
+            return;
+        if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
             LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId);
             mCurrentId = mSymbolsShiftedId;
             mInputView.setKeyboard(symbolsShiftedKeyboard);
@@ -390,7 +397,7 @@
             symbolsShiftedKeyboard.setShiftLocked(true);
             symbolsShiftedKeyboard.setImeOptions(mInputMethodService.getResources(),
                     mMode, mImeOptions);
-        } else if (mCurrentId.equals(mSymbolsShiftedId)) {
+        } else {
             LatinKeyboard symbolsKeyboard = getKeyboard(mSymbolsId);
             mCurrentId = mSymbolsId;
             mInputView.setKeyboard(symbolsKeyboard);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 8eeae86..7f84ed7 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -159,7 +159,7 @@
     private AlertDialog mOptionsDialog;
     private AlertDialog mVoiceWarningDialog;
 
-    KeyboardSwitcher mKeyboardSwitcher;
+    /* package */ KeyboardSwitcher mKeyboardSwitcher;
 
     private UserDictionary mUserDictionary;
     private UserBigramDictionary mUserBigramDictionary;
@@ -168,7 +168,7 @@
 
     private Hints mHints;
 
-    Resources mResources;
+    private Resources mResources;
 
     private String mInputLocale;
     private String mSystemLocale;
@@ -307,7 +307,7 @@
         }
     }
 
-    Handler mHandler = new Handler() {
+    /* package */ Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -339,7 +339,8 @@
         }
     };
 
-    @Override public void onCreate() {
+    @Override
+    public void onCreate() {
         LatinImeLogger.init(this);
         super.onCreate();
         //setStatusIcon(R.drawable.ime_qwerty);
@@ -396,7 +397,7 @@
      * Loads a dictionary or multiple separated dictionary
      * @return returns array of dictionary resource ids
      */
-    static int[] getDictionary(Resources res) {
+    /* package */ static int[] getDictionary(Resources res) {
         String packageName = LatinIME.class.getPackage().getName();
         XmlResourceParser xrp = res.getXml(R.xml.dictionary);
         ArrayList<Integer> dictionaries = new ArrayList<Integer>();
@@ -1013,9 +1014,6 @@
     }
 
     private void reloadKeyboards() {
-        if (mKeyboardSwitcher == null) {
-            mKeyboardSwitcher = new KeyboardSwitcher(this);
-        }
         mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
         if (mKeyboardSwitcher.getInputView() != null
                 && mKeyboardSwitcher.getKeyboardMode() != KeyboardSwitcher.MODE_NONE) {
@@ -2406,20 +2404,20 @@
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
     }
 
-    void tutorialDone() {
+    /* package */ void tutorialDone() {
         mTutorial = null;
     }
 
-    void promoteToUserDictionary(String word, int frequency) {
+    /* package */ void promoteToUserDictionary(String word, int frequency) {
         if (mUserDictionary.isValidWord(word)) return;
         mUserDictionary.addWord(word, frequency);
     }
 
-    WordComposer getCurrentWord() {
+    /* package */ WordComposer getCurrentWord() {
         return mWord;
     }
 
-    boolean getPopupOn() {
+    /* package */ boolean getPopupOn() {
         return mPopupOn;
     }
 
@@ -2576,7 +2574,8 @@
         return list;
     }
 
-    @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         super.dump(fd, fout, args);
 
         final Printer p = new PrintWriterPrinter(fout);
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
index bcd1bb0..f96a359 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
@@ -47,10 +47,9 @@
 import android.widget.TextView;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * A view that renders a virtual {@link LatinKeyboard}. It handles rendering of keys and
@@ -199,7 +198,7 @@
     private PopupWindow mMiniKeyboardPopup;
     private LatinKeyboardBaseView mMiniKeyboard;
     private View mMiniKeyboardParent;
-    private Map<Key,View> mMiniKeyboardCache;
+    private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
     private int mMiniKeyboardOriginX;
     private int mMiniKeyboardOriginY;
     private long mMiniKeyboardPopupTime;
@@ -489,7 +488,6 @@
         mPaint.setAlpha(255);
 
         mPadding = new Rect(0, 0, 0, 0);
-        mMiniKeyboardCache = new HashMap<Key,View>();
         mKeyBackground.getPadding(mPadding);
 
         mSwipeThreshold = (int) (500 * res.getDisplayMetrics().density);