Merge "Add a first version of Hebrew and Arabic keyboard."
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java
index 1574466..fea15fd 100644
--- a/java/src/com/android/inputmethod/compat/CompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/CompatUtils.java
@@ -94,7 +94,7 @@
         }
     }
 
-    public static List<InputMethodSubtypeCompatWrapper> copyInputMethodSubtypeListToWrappler(
+    public static List<InputMethodSubtypeCompatWrapper> copyInputMethodSubtypeListToWrapper(
             Object listObject) {
         if (!(listObject instanceof List<?>)) return null;
         final List<InputMethodSubtypeCompatWrapper> subtypes =
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index 648b189..81cf02c 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -71,7 +71,7 @@
             InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes) {
         Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList,
                 imi, allowsImplicitlySelectedSubtypes);
-        return CompatUtils.copyInputMethodSubtypeListToWrappler((List<?>)retval);
+        return CompatUtils.copyInputMethodSubtypeListToWrapper((List<?>)retval);
     }
 
     public Map<InputMethodInfo, List<InputMethodSubtypeCompatWrapper>>
@@ -86,7 +86,7 @@
                 Log.e(TAG, "Class type error.");
                 return null;
             }
-            shortcutMap.put((InputMethodInfo)key, CompatUtils.copyInputMethodSubtypeListToWrappler(
+            shortcutMap.put((InputMethodInfo)key, CompatUtils.copyInputMethodSubtypeListToWrapper(
                     retvalMap.get(key)));
         }
         return shortcutMap;
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
new file mode 100644
index 0000000..72f3ca0
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.inputmethod.compat;
+
+import com.android.inputmethod.latin.SubtypeSwitcher;
+
+import android.inputmethodservice.InputMethodService;
+import android.view.inputmethod.InputMethodSubtype;
+
+public class InputMethodServiceCompatWrapper extends InputMethodService {
+    // CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10
+    // or previous. Note that InputMethodSubtype was added in the API level 11.
+    // For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged().
+    // For the API level 10 or previous, we handle the "subtype changed" events by ourselves
+    // without having support from framework -- onCurrentInputMethodSubtypeChanged().
+    private static final boolean CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED = true;
+
+    private InputMethodManagerCompatWrapper mImm;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mImm = InputMethodManagerCompatWrapper.getInstance(this);
+    }
+
+    // When the API level is 10 or previous, notifyOnCurrentInputMethodSubtypeChanged should
+    // handle the event the current subtype was changed. LatinIME calls
+    // notifyOnCurrentInputMethodSubtypeChanged every time LatinIME
+    // changes the current subtype.
+    // This call is required to let LatinIME itself know a subtype changed
+    // event when the API level is 10 or previous.
+    @SuppressWarnings("unused")
+    public void notifyOnCurrentInputMethodSubtypeChanged(InputMethodSubtypeCompatWrapper subtype) {
+        // Do nothing when the API level is 11 or later
+        if (CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) return;
+        if (subtype == null) {
+            subtype = mImm.getCurrentInputMethodSubtype();
+        }
+        if (subtype != null) {
+            SubtypeSwitcher.getInstance().updateSubtype(subtype);
+        }
+    }
+
+    @Override
+    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
+        // Do nothing when the API level is 10 or previous
+        if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) return;
+        SubtypeSwitcher.getInstance().updateSubtype(
+                new InputMethodSubtypeCompatWrapper(subtype));
+    }
+}
diff --git a/java/src/com/android/inputmethod/deprecated/VoiceConnector.java b/java/src/com/android/inputmethod/deprecated/VoiceConnector.java
index 5c78e9d..4ea2d1a 100644
--- a/java/src/com/android/inputmethod/deprecated/VoiceConnector.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceConnector.java
@@ -566,14 +566,24 @@
 
             @Override
             protected void onPostExecute(Boolean result) {
+                // Calls in this method need to be done in the same thread as the thread which
+                // called switchToLastInputMethod()
                 if (!result) {
                     if (DEBUG) {
                         Log.d(TAG, "Couldn't switch back to last IME.");
                     }
-                    // Needs to reset here because LatinIME failed to back to any IME and
-                    // the same voice subtype will be triggered in the next time.
+                    // Because the current IME and subtype failed to switch to any other IME and
+                    // subtype by switchToLastInputMethod, the current IME and subtype should keep
+                    // being LatinIME and voice subtype in the next time. And for re-showing voice
+                    // mode, the state of voice input should be reset and the voice view should be
+                    // hidden.
                     mVoiceInput.reset();
                     mService.requestHideSelf(0);
+                } else {
+                    // Notify an event that the current subtype was changed. This event will be
+                    // handled if "onCurrentInputMethodSubtypeChanged" can't be implemented
+                    // when the API level is 10 or previous.
+                    mService.notifyOnCurrentInputMethodSubtypeChanged(null);
                 }
             }
         }.execute();
@@ -630,6 +640,7 @@
     }
 
     private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) {
+        @SuppressWarnings("deprecation")
         final boolean noMic = Utils.inPrivateImeOptions(null,
                 LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, attribute)
                 || Utils.inPrivateImeOptions(mService.getPackageName(),
@@ -782,8 +793,8 @@
         public void setVoiceInput(VoiceInput voiceInput, SubtypeSwitcher switcher) {
             if (mVoiceInput == null && voiceInput != null) {
                 mVoiceInput = voiceInput;
-                switcher.setVoiceInputConnector(this);
             }
+            switcher.setVoiceInputConnector(this);
         }
 
         private VoiceInputConnector() {
diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
index 7a3bcd8..c786462 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
@@ -56,7 +56,6 @@
             if (null == providers) return; // No providers : it is not a dictionary.
 
             // Search for some dictionary pack in the just-installed package. If found, reread.
-            boolean found = false;
             for (ProviderInfo info : providers) {
                 if (BinaryDictionary.DICTIONARY_PACK_AUTHORITY.equals(info.authority)) {
                     mService.resetSuggestMainDict();
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b34d457..b89fcbf 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -18,7 +18,7 @@
 
 import com.android.inputmethod.compat.CompatUtils;
 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
 import com.android.inputmethod.deprecated.VoiceConnector;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
@@ -69,7 +69,6 @@
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodSubtype;
 import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
@@ -83,7 +82,7 @@
 /**
  * Input method implementation for Qwerty'ish keyboard.
  */
-public class LatinIME extends InputMethodService implements KeyboardActionListener {
+public class LatinIME extends InputMethodServiceCompatWrapper implements KeyboardActionListener {
     private static final String TAG = LatinIME.class.getSimpleName();
     private static final boolean PERF_DEBUG = false;
     private static final boolean TRACE = false;
@@ -96,6 +95,7 @@
      *
      * @deprecated Use {@link LatinIME#IME_OPTION_NO_MICROPHONE} with package name prefixed.
      */
+    @SuppressWarnings("dep-ann")
     public static final String IME_OPTION_NO_MICROPHONE_COMPAT = "nm";
 
     /**
@@ -2339,9 +2339,4 @@
         for (int i = 0; i < CPS_BUFFER_SIZE; i++) total += mCpsIntervals[i];
         System.out.println("CPS = " + ((CPS_BUFFER_SIZE * 1000f) / total));
     }
-
-    @Override
-    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
-        SubtypeSwitcher.getInstance().updateSubtype(new InputMethodSubtypeCompatWrapper(subtype));
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 4bdd015..d4db7d0 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -31,6 +31,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.os.AsyncTask;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.Log;
@@ -308,12 +309,24 @@
         }
         final String imiId = mShortcutInputMethodInfo.getId();
         final InputMethodSubtypeCompatWrapper subtype = mShortcutSubtype;
-        new Thread("SwitchToShortcutIME") {
+        new AsyncTask<Void, Void, Void>() {
             @Override
-            public void run() {
+            protected Void doInBackground(Void... params) {
                 mImm.setInputMethodAndSubtype(token, imiId, subtype);
+                return null;
             }
-        }.start();
+
+            @Override
+            protected void onPostExecute(Void result) {
+                // Calls in this method need to be done in the same thread as the thread which
+                // called switchToShortcutIME().
+
+                // Notify an event that the current subtype was changed. This event will be
+                // handled if "onCurrentInputMethodSubtypeChanged" can't be implemented
+                // when the API level is 10 or previous.
+                mService.notifyOnCurrentInputMethodSubtypeChanged(subtype);
+            }
+        }.execute();
     }
 
     public Drawable getShortcutIcon() {