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

Commit 4e4569da authored by satok's avatar satok
Browse files

Add an API to get shortcut IMEs

- If there are no selected shortcut IMEs, the most applicable voice input will be selected as a shortcut IME

Change-Id: Ibd0f7ef5101013569c303820a3adc9038a97356d
parent e45674e2
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -156003,22 +156003,22 @@
 visibility="public"
>
</field>
<field name="GROUP_VISIBLE"
<field name="GROUP_IS_READ_ONLY"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;group_visible&quot;"
 value="&quot;group_is_read_only&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="GROUP_IS_READ_ONLY"
<field name="GROUP_VISIBLE"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;group_is_read_only&quot;"
 value="&quot;group_visible&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
@@ -220894,6 +220894,17 @@
 visibility="public"
>
</method>
<method name="getShortcutInputMethodsAndSubtypes"
 return="java.util.List&lt;android.util.Pair&lt;android.view.inputmethod.InputMethodInfo, android.view.inputmethod.InputMethodSubtype&gt;&gt;"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="hideSoftInputFromInputMethod"
 return="void"
 abstract="false"
@@ -248473,7 +248484,7 @@
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="t" type="T">
<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
+34 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -47,6 +48,7 @@ import com.android.internal.view.InputBindResult;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -1452,6 +1454,38 @@ public final class InputMethodManager {
        }
    }

    public List<Pair<InputMethodInfo, InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
        synchronized (mH) {
            List<Pair<InputMethodInfo, InputMethodSubtype>> ret =
                    new ArrayList<Pair<InputMethodInfo, InputMethodSubtype>>();
            try {
                // TODO: We should change the return type from List<Object> to List<Parcelable>
                List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
                // "info" has imi1, subtype1, imi2, subtype2, imi3, subtype3,..... in the list
                Object imi;
                Object subtype;
                if (info != null && info.size() > 0) {
                    final int N = info.size();
                    if (N % 2 == 0) {
                        for (int i = 0; i < N;) {
                            if ((imi = info.get(i++)) instanceof InputMethodInfo) {
                                subtype = info.get(i++);
                                ret.add(new Pair<InputMethodInfo, InputMethodSubtype> (
                                        (InputMethodInfo)imi,
                                        (subtype instanceof InputMethodSubtype) ?
                                                (InputMethodSubtype)subtype : null));
                            }
                        }
                    } else {
                        Log.w(TAG, "The size of list was illegal.");
                    }
                }
            } catch (RemoteException e) {
                Log.w(TAG, "IME died: " + mCurId, e);
            }
            return ret;
        }
    }
    public boolean switchToLastInputMethod(IBinder imeToken) {
        synchronized (mH) {
            try {
+3 −0
Original line number Diff line number Diff line
@@ -32,6 +32,9 @@ interface IInputMethodManager {
    List<InputMethodInfo> getInputMethodList();
    List<InputMethodInfo> getEnabledInputMethodList();
    List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in InputMethodInfo imi);
    // TODO: We should change the return type from List to List<Parcelable>
    // Currently there is a bug that aidl doesn't accept List<Parcelable>
    List getShortcutInputMethodsAndSubtypes();
    void addClient(in IInputMethodClient client,
            in IInputContext inputContext, int uid, int pid);
    void removeClient(in IInputMethodClient client);
+133 −22
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import android.os.IBinder;
import android.os.IInterface;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -120,6 +121,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
    // If IME doesn't support the system locale, the default subtype will be the first defined one.
    private static final int DEFAULT_SUBTYPE_ID = 0;

    private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
    private static final String SUBTYPE_MODE_VOICE = "voice";

    final Context mContext;
    final Handler mHandler;
    final InputMethodSettings mSettings;
@@ -235,6 +239,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
     */
    private InputMethodSubtype mCurrentSubtype;

    // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
    private List<Pair<InputMethodInfo, InputMethodSubtype>> mShortcutInputMethodsAndSubtypes;
    // This list is used for returning the pairs of InputMethodInfo and InputMethodSubtype through
    // aidl. This list has imi1, subtype1 imi2, subtype2...
    private List mShortcutInputMethodsAndSubtypesObjectList;

    /**
     * Set to true if our ServiceConnection is currently actively bound to
@@ -983,6 +992,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                mCurMethodId = null;
                unbindCurrentMethodLocked(true);
            }
            mShortcutInputMethodsAndSubtypes = null;
        } else {
            // There is no longer an input method set, so stop any current one.
            mCurMethodId = null;
@@ -1910,25 +1920,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        return NOT_A_SUBTYPE_ID;
    }

    // If there are no selected subtypes, tries finding the most applicable one according to the
    // current system locale
    private int findApplicableSubtypeLocked(String id) {
        InputMethodInfo imi = mMethodMap.get(id);
        if (imi == null) {
            return NOT_A_SUBTYPE_ID;
        }
        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
    /**
     * If there are no selected subtypes, tries finding the most applicable one according to the
     * given locale.
     * @param subtypes this function will search the most applicable subtype in subtypes
     * @param mode subtypes will be filtered by mode
     * @param locale subtypes will be filtered by locale
     * @param defaultSubtypeId if this function can't find the most applicable subtype, it will
     * return defaultSubtypeId
     * @return the most applicable subtypeId
     */
    private int findLastResortApplicableSubtypeLocked(
            List<InputMethodSubtype> subtypes, String mode, String locale, int defaultSubtypeId) {
        if (subtypes == null || subtypes.size() == 0) {
            return NOT_A_SUBTYPE_ID;
        }
        final String locale = mContext.getResources().getConfiguration().locale.toString();
        if (TextUtils.isEmpty(locale)) {
            locale = mContext.getResources().getConfiguration().locale.toString();
        }
        final String language = locale.substring(0, 2);
        boolean partialMatchFound = false;
        int applicableSubtypeId = DEFAULT_SUBTYPE_ID;
        int applicableSubtypeId = defaultSubtypeId;
        for (int i = 0; i < subtypes.size(); ++i) {
            final String subtypeLocale = subtypes.get(i).getLocale();
            // An applicable subtype should be a keyboard subtype
            if (subtypes.get(i).getMode().equalsIgnoreCase("keyboard")) {
            // An applicable subtype should match "mode".
            if (subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
                if (locale.equals(subtypeLocale)) {
                    // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
                    applicableSubtypeId = i;
@@ -1950,34 +1966,129 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        return applicableSubtypeId;
    }

    // If there are no selected shortcuts, tries finding the most applicable ones.
    private Pair<InputMethodInfo, InputMethodSubtype>
            findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
        List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
        InputMethodInfo mostApplicableIMI = null;
        int mostApplicableSubtypeId = NOT_A_SUBTYPE_ID;
        boolean foundInSystemIME = false;

        // Search applicable subtype for each InputMethodInfo
        for (InputMethodInfo imi: imis) {
            int subtypeId = NOT_A_SUBTYPE_ID;
            if (mCurrentSubtype != null) {
                // 1. Search with the current subtype's locale and the enabled subtypes
                subtypeId = findLastResortApplicableSubtypeLocked(
                        mSettings.getEnabledInputMethodSubtypeListLocked(
                        imi), mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
                if (subtypeId == NOT_A_SUBTYPE_ID) {
                    // 2. Search with the current subtype's locale and all subtypes
                    subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
                            mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
                }
            }
            // 3. Search with the system locale and the enabled subtypes
            if (subtypeId == NOT_A_SUBTYPE_ID) {
                subtypeId = findLastResortApplicableSubtypeLocked(
                        mSettings.getEnabledInputMethodSubtypeListLocked(
                        imi), mode, null, NOT_A_SUBTYPE_ID);
            }
            if (subtypeId == NOT_A_SUBTYPE_ID) {
                // 4. Search with the system locale and all subtypes
                subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
                        mode, null, NOT_A_SUBTYPE_ID);
            }
            if (subtypeId != NOT_A_SUBTYPE_ID) {
                if (imi.getId().equals(mCurMethodId)) {
                    // The current input method is the most applicable IME.
                    mostApplicableIMI = imi;
                    mostApplicableSubtypeId = subtypeId;
                    break;
                } else if ((imi.getServiceInfo().applicationInfo.flags
                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
                    // The system input method is 2nd applicable IME.
                    mostApplicableIMI = imi;
                    mostApplicableSubtypeId = subtypeId;
                    foundInSystemIME = true;
                } else if (!foundInSystemIME) {
                    mostApplicableIMI = imi;
                    mostApplicableSubtypeId = subtypeId;
                }
            }
        }
        if (DEBUG) {
            Slog.w(TAG, "Most applicable shortcut input method subtype was:"
                    + mostApplicableIMI.getId() + "," + mostApplicableSubtypeId);
        }
        if (mostApplicableIMI != null && mostApplicableSubtypeId != NOT_A_SUBTYPE_ID) {
            ArrayList<Parcelable> ret = new ArrayList<Parcelable>(2);
            return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
                    mostApplicableIMI.getSubtypes().get(mostApplicableSubtypeId));
        } else {
            return null;
        }
    }

    /**
     * @return Return the current subtype of this input method.
     */
    public InputMethodSubtype getCurrentInputMethodSubtype() {
        synchronized (mMethodMap) {
        boolean subtypeIsSelected = false;
        try {
            subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(),
                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID;
        } catch (SettingNotFoundException e) {
        }
        synchronized (mMethodMap) {
            if (!subtypeIsSelected || mCurrentSubtype == null) {
                String lastInputMethodId =
                        Settings.Secure.getString(mContext.getContentResolver(),
                                Settings.Secure.DEFAULT_INPUT_METHOD);
                String lastInputMethodId = Settings.Secure.getString(
                        mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
                int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
                if (subtypeId == NOT_A_SUBTYPE_ID) {
                    subtypeId = findApplicableSubtypeLocked(lastInputMethodId);
                    InputMethodInfo imi = mMethodMap.get(lastInputMethodId);
                    if (imi != null) {
                        // If there are no selected subtypes, the framework will try to find
                        // the most applicable subtype from all subtypes whose mode is
                        // SUBTYPE_MODE_KEYBOARD. This is an exceptional case, so we will hardcode
                        // the mode.
                        subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
                                SUBTYPE_MODE_KEYBOARD, null, DEFAULT_SUBTYPE_ID);
                    }
                }
                if (subtypeId != NOT_A_SUBTYPE_ID) {
                    mCurrentSubtype =
                            mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
                } else {
                    mCurrentSubtype = null;
                }
            }
            return mCurrentSubtype;
        }
    }

    // TODO: We should change the return type from List to List<Parcelable>
    public List getShortcutInputMethodsAndSubtypes() {
        synchronized (mMethodMap) {
            if (mShortcutInputMethodsAndSubtypesObjectList != null) {
                // If there are no selected shortcut subtypes, the framework will try to find
                // the most applicable subtype from all subtypes whose mode is
                // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
                mShortcutInputMethodsAndSubtypes =
                        new ArrayList<Pair<InputMethodInfo, InputMethodSubtype>>();
                mShortcutInputMethodsAndSubtypes.add(
                        findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
                                SUBTYPE_MODE_VOICE));
                mShortcutInputMethodsAndSubtypesObjectList = new ArrayList<Parcelable>();
                for (Pair ime: mShortcutInputMethodsAndSubtypes) {
                    mShortcutInputMethodsAndSubtypesObjectList.add(ime.first);
                    mShortcutInputMethodsAndSubtypesObjectList.add(ime.second);
                }
            }
            return mShortcutInputMethodsAndSubtypesObjectList;
        }
    }

    public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
        synchronized (mMethodMap) {
            if (subtype != null && mCurMethodId != null) {