From 8e09172df1bb176cc899940862c56bed9b9aec4e Mon Sep 17 00:00:00 2001
From: satok <satok@google.com>
Date: Sun, 21 Nov 2010 12:43:10 +0900
Subject: [PATCH] Enabled to change the current subtype to voice input subtypes

bug: 3201828

Change-Id: I4450cbc8ba8829060d702564889c735f1326cf23
---
 .../android/inputmethod/latin/LatinIME.java   |  34 ++++--
 .../inputmethod/latin/SubtypeSwitcher.java    | 114 ++++++++++++++++--
 .../inputmethod/voice/VoiceIMEConnector.java  |  30 ++++-
 3 files changed, 153 insertions(+), 25 deletions(-)

diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d624ae4449..4989d8e3c3 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -35,6 +35,7 @@ import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.SystemClock;
 import android.os.Vibrator;
@@ -48,6 +49,8 @@ import android.util.Printer;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.inputmethod.CompletionInfo;
@@ -458,7 +461,7 @@ public class LatinIME extends InputMethodService
     @Override
     public void onConfigurationChanged(Configuration conf) {
         mSubtypeSwitcher.onConfigurationChanged(conf);
-        onLanguageChanged();
+        onKeyboardLanguageChanged();
         updateAutoTextEnabled();
 
         // If orientation changed while predicting, commit the change
@@ -512,6 +515,7 @@ public class LatinIME extends InputMethodService
     public void onStartInputView(EditorInfo attribute, boolean restarting) {
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         LatinKeyboardView inputView = switcher.getInputView();
+
         // In landscape mode, this method gets called without the input view being created.
         if (inputView == null) {
             return;
@@ -521,7 +525,7 @@ public class LatinIME extends InputMethodService
 
         if (mRefreshKeyboardRequired) {
             mRefreshKeyboardRequired = false;
-            onLanguageChanged();
+            onKeyboardLanguageChanged();
         }
 
         TextEntryState.newSession(this);
@@ -629,6 +633,10 @@ public class LatinIME extends InputMethodService
         checkReCorrectionOnStart();
         checkTutorial(attribute.privateImeOptions);
         inputView.setForeground(true);
+
+        // TODO: Not to show keyboard if IME starts in Voice One shot mode.
+        mVoiceConnector.onStartInputView();
+
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
     }
 
@@ -709,10 +717,8 @@ public class LatinIME extends InputMethodService
         // If the current selection in the text view changes, we should
         // clear whatever candidate text we have.
         if ((((mComposing.length() > 0 && mPredicting)
-                || mVoiceConnector.isVoiceInputHighlighted())
-                && (newSelStart != candidatesEnd
-                    || newSelEnd != candidatesEnd)
-                && mLastSelectionStart != newSelStart)) {
+                || mVoiceConnector.isVoiceInputHighlighted()) && (newSelStart != candidatesEnd
+                        || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart)) {
             mComposing.setLength(0);
             mPredicting = false;
             mHandler.postUpdateSuggestions();
@@ -1418,8 +1424,17 @@ public class LatinIME extends InputMethodService
     public void switchToKeyboardView() {
       mHandler.post(new Runnable() {
           public void run() {
-              if (mKeyboardSwitcher.getInputView() != null) {
-                  setInputView(mKeyboardSwitcher.getInputView());
+              if (DEBUG) {
+                  Log.d(TAG, "Switch to keyboard view.");
+              }
+              View v = mKeyboardSwitcher.getInputView();
+              if (v != null) {
+                  // Confirms that the keyboard view doesn't have parent view.
+                  ViewParent p = v.getParent();
+                  if (p != null && p instanceof ViewGroup) {
+                      ((ViewGroup)p).removeView(v);
+                  }
+                  setInputView(v);
               }
               setCandidatesViewShown(isCandidateStripVisible());
               updateInputViewShown();
@@ -1844,7 +1859,7 @@ public class LatinIME extends InputMethodService
 
     // Notify that Language has been changed and toggleLanguage will update KeyboaredID according
     // to new Language.
-    private void onLanguageChanged() {
+    public void onKeyboardLanguageChanged() {
         toggleLanguage(true, true);
     }
 
@@ -2226,6 +2241,5 @@ public class LatinIME extends InputMethodService
     @Override
     public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
         SubtypeSwitcher.getInstance().updateSubtype(subtype);
-        onLanguageChanged();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index fe629949c3..df123c9f46 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -17,12 +17,12 @@
 package com.android.inputmethod.latin;
 
 import com.android.inputmethod.voice.SettingsUtil;
+import com.android.inputmethod.voice.VoiceInput;
 
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.inputmethodservice.InputMethodService;
 import android.preference.PreferenceManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -39,7 +39,7 @@ public class SubtypeSwitcher {
     // We may or may not draw the current language on space bar regardless of this flag.
     public static final boolean USE_SPACEBAR_LANGUAGE_SWITCHER = false;
     private static final boolean DBG = false;
-    private static final String TAG = "SubtypeSwitcher";
+    private static final String TAG = "InputMethodSubtypeSwitcher";
 
     private static final char LOCALE_SEPARATER = '_';
     private static final String KEYBOARD_MODE = "keyboard";
@@ -48,7 +48,7 @@ public class SubtypeSwitcher {
             new TextUtils.SimpleStringSplitter(LOCALE_SEPARATER);
 
     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
-    private /* final */ InputMethodService mService;
+    private /* final */ LatinIME mService;
     private /* final */ InputMethodManager mImm;
     private /* final */ Resources mResources;
     private final ArrayList<InputMethodSubtype> mEnabledKeyboardSubtypesOfCurrentInputMethod =
@@ -62,6 +62,7 @@ public class SubtypeSwitcher {
     private String mInputLocaleStr;
     private String mMode;
     private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
+    private VoiceInput mVoiceInput;
     private boolean mNeedsToDisplayLanguage;
     private boolean mIsSystemLanguageSameAsInputLanguage;
     /*-----------------------------------------------------------*/
@@ -71,10 +72,7 @@ public class SubtypeSwitcher {
     }
 
     public static void init(LatinIME service) {
-        sInstance.mService = service;
-        sInstance.mResources = service.getResources();
-        sInstance.mImm = (InputMethodManager) service.getSystemService(
-                Context.INPUT_METHOD_SERVICE);
+        sInstance.resetParams(service);
         if (USE_SPACEBAR_LANGUAGE_SWITCHER) {
             sInstance.initLanguageSwitcher(service);
         }
@@ -85,6 +83,21 @@ public class SubtypeSwitcher {
     private SubtypeSwitcher() {
     }
 
+    private void resetParams(LatinIME service) {
+        mService = service;
+        mResources = service.getResources();
+        mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE);
+        mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
+        mEnabledLanguagesOfCurrentInputMethod.clear();
+        mSystemLocale = null;
+        mInputLocale = null;
+        mInputLocaleStr = null;
+        mMode = null;
+        mAllEnabledSubtypesOfCurrentInputMethod = null;
+        // TODO: Voice input should be created here
+        mVoiceInput = null;
+    }
+
     // Update all parameters stored in SubtypeSwitcher.
     // Only configuration changed event is allowed to call this because this is heavy.
     private void updateAllParameters() {
@@ -126,17 +139,65 @@ public class SubtypeSwitcher {
         mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
                 && mIsSystemLanguageSameAsInputLanguage);
         if (foundCurrentSubtypeBecameDisabled) {
+            if (DBG) {
+                Log.w(TAG, "Last subtype was disabled. Update to the current one.");
+            }
             updateSubtype(mImm.getCurrentInputMethodSubtype());
         }
     }
 
     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
     public void updateSubtype(InputMethodSubtype newSubtype) {
+        final String newLocale;
+        final String newMode;
+        if (newSubtype == null) {
+            // Normally, newSubtype shouldn't be null. But just in case if newSubtype was null,
+            // fallback to the default locale and mode.
+            Log.e(TAG, "Couldn't get the current subtype.");
+            newLocale = "en_US";
+            newMode =KEYBOARD_MODE;
+        } else {
+            newLocale = newSubtype.getLocale();
+            newMode = newSubtype.getMode();
+        }
         if (DBG) {
-            Log.w(TAG, "Update subtype to:" + newSubtype.getLocale() + "," + newSubtype.getMode());
+            Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode
+                    + ", from: " + mInputLocaleStr + ", " + mMode);
+        }
+        boolean languageChanged = false;
+        if (!newLocale.equals(mInputLocaleStr)) {
+            if (mInputLocaleStr != null) {
+                languageChanged = true;
+            }
+            updateInputLocale(newLocale);
+        }
+        boolean modeChanged = false;
+        String oldMode = mMode;
+        if (!newMode.equals(mMode)) {
+            if (mMode != null) {
+                modeChanged = true;
+            }
+            mMode = newMode;
+        }
+        if (isKeyboardMode()) {
+            if (modeChanged) {
+                if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
+                    mVoiceInput.cancel();
+                }
+            }
+            if (languageChanged) {
+                mService.onKeyboardLanguageChanged();
+            }
+        } else if (isVoiceMode()) {
+            if (languageChanged || modeChanged) {
+                if (mVoiceInput != null) {
+                    // TODO: Call proper function to trigger VoiceIME
+                    mService.onKey(LatinKeyboardView.KEYCODE_VOICE, null, 0, 0);
+                }
+            }
+        } else {
+            Log.w(TAG, "Unknown subtype mode: " + mMode);
         }
-        updateInputLocale(newSubtype.getLocale());
-        mMode = newSubtype.getMode();
     }
 
     // Update the current input locale from Locale string.
@@ -269,6 +330,37 @@ public class SubtypeSwitcher {
         return oldLocale;
     }
 
+    public boolean isKeyboardMode() {
+        return KEYBOARD_MODE.equals(mMode);
+    }
+
+
+    ///////////////////////////
+    // Voice Input functions //
+    ///////////////////////////
+
+    public boolean setVoiceInput(VoiceInput vi) {
+        if (mVoiceInput == null) {
+            // TODO: Remove requirements to construct KeyboardSwitcher
+            // when IME was enabled with Voice mode
+            mService.onKeyboardLanguageChanged();
+            mVoiceInput = vi;
+            if (isVoiceMode() && mVoiceInput != null) {
+                if (DBG) {
+                    Log.d(TAG, "Set and call voice input.");
+                }
+                // TODO: Call proper function to enable VoiceIME
+                mService.onKey(LatinKeyboardView.KEYCODE_VOICE, null, 0, 0);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isVoiceMode() {
+        return VOICE_MODE.equals(mMode);
+    }
+
     //////////////////////////////////////
     // SpaceBar Language Switch support //
     //////////////////////////////////////
@@ -383,4 +475,4 @@ public class SubtypeSwitcher {
         mLanguageSwitcher.loadLocales(prefs);
         mLanguageSwitcher.setSystemLocale(conf.locale);
     }
-}
\ No newline at end of file
+}
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
index 23b6752eff..629d4b45e5 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
@@ -41,6 +41,7 @@ import android.view.WindowManager;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -78,12 +79,14 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
     private boolean mVoiceButtonOnPrimary;
     private boolean mVoiceInputHighlighted;
 
+    private InputMethodManager mImm;
     private LatinIME mContext;
     private AlertDialog mVoiceWarningDialog;
     private VoiceInput mVoiceInput;
     private final VoiceResults mVoiceResults = new VoiceResults();
     private Hints mHints;
     private UIHandler mHandler;
+    private SubtypeSwitcher mSubtypeSwitcher;
     // For each word, a list of potential replacements, usually from voice.
     private final Map<String, List<CharSequence>> mWordToSuggestions =
             new HashMap<String, List<CharSequence>>();
@@ -96,6 +99,8 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
     private void initInternal(LatinIME context, UIHandler h) {
         mContext = context;
         mHandler = h;
+        mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         if (VOICE_INSTALLED) {
             mVoiceInput = new VoiceInput(context, this);
             mHints = new Hints(context, new Hints.Display() {
@@ -376,7 +381,6 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
             }
         }
         mContext.vibrate();
-        mContext.switchToKeyboardView();
 
         final List<CharSequence> nBest = new ArrayList<CharSequence>();
         for (String c : mVoiceResults.candidates) {
@@ -399,6 +403,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
 
         mVoiceInputHighlighted = true;
         mWordToSuggestions.putAll(mVoiceResults.alternatives);
+        onCancelVoice();
     }
 
     public void switchToRecognitionStatusView(final boolean configurationChanging) {
@@ -410,7 +415,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
                 View v = mVoiceInput.getView();
                 ViewParent p = v.getParent();
                 if (p != null && p instanceof ViewGroup) {
-                    ((ViewGroup)v.getParent()).removeView(v);
+                    ((ViewGroup)p).removeView(v);
                 }
                 mContext.setInputView(v);
                 mContext.updateInputViewShown();
@@ -499,6 +504,10 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
         }
     }
 
+    public void onStartInputView() {
+        mSubtypeSwitcher.setVoiceInput(mVoiceInput);
+    }
+
     public void onConfigurationChanged(boolean configurationChanging) {
         if (mRecognizing) {
             switchToRecognitionStatusView(configurationChanging);
@@ -507,8 +516,21 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
     @Override
     public void onCancelVoice() {
         if (mRecognizing) {
-            mRecognizing = false;
-            mContext.switchToKeyboardView();
+            if (mSubtypeSwitcher.isVoiceMode()) {
+                // If voice mode is being canceled within LatinIME (i.e. time-out or user
+                // cancellation etc.), onCancelVoice() will be called first. LatinIME thinks it's
+                // still in voice mode. LatinIME needs to call switchToLastInputMethod().
+                // Note that onCancelVoice() will be called again from SubtypeSwitcher.
+                IBinder token = mContext.getWindow().getWindow().getAttributes().token;
+                mImm.switchToLastInputMethod(token);
+            } else if (mSubtypeSwitcher.isKeyboardMode()) {
+                // If voice mode is being canceled out of LatinIME (i.e. by user's IME switching or
+                // as a result of switchToLastInputMethod() etc.),
+                // onCurrentInputMethodSubtypeChanged() will be called first. LatinIME will know
+                // that it's in keyboard mode and SubtypeSwitcher will call onCancelVoice().
+                mRecognizing = false;
+                mContext.switchToKeyboardView();
+            }
         }
     }
 
-- 
GitLab