Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74290098 authored by satok's avatar satok
Browse files

Enable VoiceInput even if there is no shortcut subtype supported

Change-Id: I1d455348f56d73ecb942f22c2bbd03f240b489a6
parent a1f9cdd6
Loading
Loading
Loading
Loading
+78 −7
Original line number Diff line number Diff line
@@ -16,8 +16,13 @@

package com.android.inputmethod.compat;

import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.Utils;

import android.content.Context;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -27,6 +32,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

// TODO: Override this class with the concrete implementation if we need to take care of the
@@ -50,7 +56,15 @@ public class InputMethodManagerCompatWrapper {
    private static final InputMethodManagerCompatWrapper sInstance =
            new InputMethodManagerCompatWrapper();

    // For the compatibility, IMM will create dummy subtypes if subtypes are not found.
    // This is required to be false if the current behavior is broken. For now, it's ok to be true.
    private static final boolean ALLOW_DUMMY_SUBTYPE = true;
    private static final boolean HAS_VOICE_FUNCTION = true;
    private static final String VOICE_MODE = "voice";
    private static final String KEYBOARD_MODE = "keyboard";

    private InputMethodManager mImm;
    private String mLatinImePackageName;
    private InputMethodManagerCompatWrapper() {
    }

@@ -64,28 +78,82 @@ public class InputMethodManagerCompatWrapper {
    private synchronized void init(Context context) {
        mImm = (InputMethodManager) context.getSystemService(
                Context.INPUT_METHOD_SERVICE);
        if (context instanceof LatinIME) {
            mLatinImePackageName = context.getPackageName();
        }
    }

    public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
        return new InputMethodSubtypeCompatWrapper(
                CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype));
        Object o = CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype);
        return new InputMethodSubtypeCompatWrapper(o);
    }

    public List<InputMethodSubtypeCompatWrapper> getEnabledInputMethodSubtypeList(
            InputMethodInfoCompatWrapper imi, boolean allowsImplicitlySelectedSubtypes) {
        Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList,
                (imi != null ? imi.getInputMethodInfo() : null), allowsImplicitlySelectedSubtypes);
        if (retval == null || !(retval instanceof List) || ((List<?>)retval).isEmpty()) {
            if (!ALLOW_DUMMY_SUBTYPE) {
                // Returns an empty list
        if (retval == null)
                return Collections.emptyList();
            }
            // Creates dummy subtypes
            List<InputMethodSubtypeCompatWrapper> subtypeList =
                    new ArrayList<InputMethodSubtypeCompatWrapper>();
            InputMethodSubtypeCompatWrapper keyboardSubtype = getLastResortSubtype(KEYBOARD_MODE);
            InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
            if (keyboardSubtype != null) {
                subtypeList.add(keyboardSubtype);
            }
            if (voiceSubtype != null) {
                subtypeList.add(voiceSubtype);
            }
            return subtypeList;
        }
        return CompatUtils.copyInputMethodSubtypeListToWrapper((List<?>)retval);
    }

    private InputMethodInfoCompatWrapper getLatinImeInputMethodInfo() {
        if (TextUtils.isEmpty(mLatinImePackageName))
            return null;
        return Utils.getInputMethodInfo(this, mLatinImePackageName);
    }

    @SuppressWarnings("unused")
    private InputMethodSubtypeCompatWrapper getLastResortSubtype(String mode) {
        if (VOICE_MODE.equals(mode) && !HAS_VOICE_FUNCTION)
            return null;
        Locale inputLocale = SubtypeSwitcher.getInstance().getInputLocale();
        if (inputLocale == null)
            return null;
        return new InputMethodSubtypeCompatWrapper(0, 0, inputLocale.toString(), mode, "");
    }

    public Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
            getShortcutInputMethodsAndSubtypes() {
        Object retval = CompatUtils.invoke(mImm, null, METHOD_getShortcutInputMethodsAndSubtypes);
        if (retval == null || !(retval instanceof Map) || ((Map<?, ?>)retval).isEmpty()) {
            if (!ALLOW_DUMMY_SUBTYPE) {
                // Returns an empty map
        if (!(retval instanceof Map)) return Collections.emptyMap();
                return Collections.emptyMap();
            }
            // Creates dummy subtypes
            InputMethodInfoCompatWrapper imi = getLatinImeInputMethodInfo();
            InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
            if (imi != null && voiceSubtype != null) {
                Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
                        shortcutMap =
                                new HashMap<InputMethodInfoCompatWrapper,
                                        List<InputMethodSubtypeCompatWrapper>>();
                List<InputMethodSubtypeCompatWrapper> subtypeList =
                        new ArrayList<InputMethodSubtypeCompatWrapper>();
                subtypeList.add(voiceSubtype);
                shortcutMap.put(imi, subtypeList);
                return shortcutMap;
            } else {
                return Collections.emptyMap();
            }
        }
        Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>> shortcutMap =
                new HashMap<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>();
        final Map<?, ?> retvalMap = (Map<?, ?>)retval;
@@ -107,6 +175,9 @@ public class InputMethodManagerCompatWrapper {
    }

    public boolean switchToLastInputMethod(IBinder token) {
        if (SubtypeSwitcher.getInstance().isDummyVoiceMode()) {
            return true;
        }
        return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToLastInputMethod, token);
    }

+53 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.text.TextUtils;
import android.util.Log;

import java.lang.reflect.Method;
import java.util.Arrays;

// TODO: Override this class with the concrete implementation if we need to take care of the
// performance.
@@ -48,35 +49,65 @@ public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper
    private static final Method METHOD_getExtraValueOf =
            CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class);

    private final int mDummyNameResId;
    private final int mDummyIconResId;
    private final String mDummyLocale;
    private final String mDummyMode;
    private final String mDummyExtraValues;

    public InputMethodSubtypeCompatWrapper(Object subtype) {
        super((CLASS_InputMethodSubtype != null && CLASS_InputMethodSubtype.isInstance(subtype))
                ? subtype : null);
        if (DBG) {
            Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper");
        }
        mDummyNameResId = 0;
        mDummyIconResId = 0;
        mDummyLocale = DEFAULT_LOCALE;
        mDummyMode = DEFAULT_MODE;
        mDummyExtraValues = "";
    }

    // Constructor for creating a dummy subtype.
    public InputMethodSubtypeCompatWrapper(int nameResId, int iconResId, String locale,
            String mode, String extraValues) {
        super(null);
        if (DBG) {
            Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper");
        }
        mDummyNameResId = nameResId;
        mDummyIconResId = iconResId;
        mDummyLocale = locale != null ? locale : "";
        mDummyMode = mode != null ? mode : "";
        mDummyExtraValues = extraValues != null ? extraValues : "";
    }

    public int getNameResId() {
        if (mObj == null) return mDummyNameResId;
        return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getNameResId);
    }

    public int getIconResId() {
        if (mObj == null) return mDummyIconResId;
        return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getIconResId);
    }

    public String getLocale() {
        if (mObj == null) return mDummyLocale;
        final String s = (String)CompatUtils.invoke(mObj, null, METHOD_getLocale);
        if (TextUtils.isEmpty(s)) return DEFAULT_LOCALE;
        return s;
    }

    public String getMode() {
        if (mObj == null) return mDummyMode;
        String s = (String)CompatUtils.invoke(mObj, null, METHOD_getMode);
        if (TextUtils.isEmpty(s)) return DEFAULT_MODE;
        return s;
    }

    public String getExtraValue() {
        if (mObj == null) return mDummyExtraValues;
        return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValue);
    }

@@ -92,10 +123,32 @@ public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper
    public boolean equals(Object o) {
        if (o instanceof InputMethodSubtypeCompatWrapper) {
            InputMethodSubtypeCompatWrapper subtype = (InputMethodSubtypeCompatWrapper)o;
            if (mObj == null) {
                // easy check of dummy subtypes
                return (mDummyNameResId == subtype.mDummyNameResId
                        && mDummyIconResId == subtype.mDummyIconResId
                        && mDummyLocale.equals(subtype.mDummyLocale)
                        && mDummyMode.equals(subtype.mDummyMode)
                        && mDummyExtraValues.equals(subtype.mDummyExtraValues));
            }
            return mObj.equals(subtype.getOriginalObject());
        } else {
            return mObj.equals(o);
        }
    }

    @Override
    public int hashCode() {
        if (mObj == null) {
            return hashCodeInternal(mDummyNameResId, mDummyIconResId, mDummyLocale,
                    mDummyMode, mDummyExtraValues);
        }
        return mObj.hashCode();
    }

    private static int hashCodeInternal(int nameResId, int iconResId, String locale,
            String mode, String extraValue) {
        return Arrays
                .hashCode(new Object[] { nameResId, iconResId, locale, mode, extraValue });
    }
}
+11 −1
Original line number Diff line number Diff line
@@ -82,6 +82,8 @@ public class VoiceProxy implements VoiceInput.UiListener {
    private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
            "has_used_voice_input_unsupported_locale";
    private static final int RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO = 6;
    // TODO: Adjusted on phones for now
    private static final int RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP = 244;

    private static final String TAG = VoiceProxy.class.getSimpleName();
    private static final boolean DEBUG = LatinImeLogger.sDBG;
@@ -99,6 +101,7 @@ public class VoiceProxy implements VoiceInput.UiListener {
    private boolean mVoiceButtonOnPrimary;
    private boolean mVoiceInputHighlighted;

    private int mMinimumVoiceRecognitionViewHeightPixel;
    private InputMethodManagerCompatWrapper mImm;
    private LatinIME mService;
    private AlertDialog mVoiceWarningDialog;
@@ -107,6 +110,7 @@ public class VoiceProxy implements VoiceInput.UiListener {
    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>>();
@@ -123,6 +127,8 @@ public class VoiceProxy implements VoiceInput.UiListener {
    private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) {
        mService = service;
        mHandler = h;
        mMinimumVoiceRecognitionViewHeightPixel = Utils.dipToPixel(
                Utils.getDipScale(service), RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP);
        mImm = InputMethodManagerCompatWrapper.getInstance(service);
        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
        if (VOICE_INSTALLED) {
@@ -542,7 +548,11 @@ public class VoiceProxy implements VoiceInput.UiListener {
                            mService.getResources().getDisplayMetrics().heightPixels;
                    final int currentHeight = popupLayout.getLayoutParams().height;
                    final int keyboardHeight = keyboardView.getHeight();
                    if (keyboardHeight > currentHeight || keyboardHeight
                    if (mMinimumVoiceRecognitionViewHeightPixel > keyboardHeight
                            || mMinimumVoiceRecognitionViewHeightPixel > currentHeight) {
                        popupLayout.getLayoutParams().height =
                            mMinimumVoiceRecognitionViewHeightPixel;
                    } else if (keyboardHeight > currentHeight || keyboardHeight
                            > (displayHeight / RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO)) {
                        popupLayout.getLayoutParams().height = keyboardHeight;
                    }
+0 −1
Original line number Diff line number Diff line
@@ -50,7 +50,6 @@ import java.util.Locale;
 * plays beeps, shows errors, etc.
 */
public class RecognitionView {
    @SuppressWarnings("unused")
    private static final String TAG = "RecognitionView";

    private Handler mUiHandler;  // Reference to UI thread
+6 −1
Original line number Diff line number Diff line
@@ -210,7 +210,7 @@ public class SubtypeSwitcher {
        final String newLocale;
        final String newMode;
        final String oldMode = getCurrentSubtypeMode();
        if (newSubtype == null || !newSubtype.hasOriginalObject()) {
        if (newSubtype == null) {
            // Normally, newSubtype shouldn't be null. But just in case newSubtype was null,
            // fallback to the default locale.
            Log.w(TAG, "Couldn't get the current subtype.");
@@ -539,6 +539,11 @@ public class SubtypeSwitcher {
        return null == mCurrentSubtype ? false : VOICE_MODE.equals(getCurrentSubtypeMode());
    }

    public boolean isDummyVoiceMode() {
        return mCurrentSubtype != null && mCurrentSubtype.getOriginalObject() == null
                && VOICE_MODE.equals(getCurrentSubtypeMode());
    }

    private void triggerVoiceIME() {
        if (!mService.isInputViewShown()) return;
        VoiceProxy.getInstance().startListening(false,
Loading