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

Commit 44783b69 authored by satok's avatar satok Committed by Android (Google) Code Review
Browse files

Merge "Enable VoiceInput even if there is no shortcut subtype supported"

parents 6f18a1fb 74290098
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