Loading core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java +230 −1 Original line number Diff line number Diff line Loading @@ -16,11 +16,24 @@ package com.android.internal.inputmethod; import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings; import android.content.Context; import android.content.pm.PackageManager; import android.text.TextUtils; import android.util.Slog; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.TreeMap; /** * InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes. Loading @@ -29,10 +42,13 @@ public class InputMethodSubtypeSwitchingController { private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName(); private static final boolean DEBUG = false; private static final int MAX_HISTORY_SIZE = 4; private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; private static class SubtypeParams { public final InputMethodInfo mImi; public final InputMethodSubtype mSubtype; public final long mTime; public SubtypeParams(InputMethodInfo imi, InputMethodSubtype subtype) { mImi = imi; mSubtype = subtype; Loading @@ -40,7 +56,199 @@ public class InputMethodSubtypeSwitchingController { } } public static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> { public final CharSequence mImeName; public final CharSequence mSubtypeName; public final InputMethodInfo mImi; public final int mSubtypeId; private final boolean mIsSystemLocale; private final boolean mIsSystemLanguage; public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName, InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) { mImeName = imeName; mSubtypeName = subtypeName; mImi = imi; mSubtypeId = subtypeId; if (TextUtils.isEmpty(subtypeLocale)) { mIsSystemLocale = false; mIsSystemLanguage = false; } else { mIsSystemLocale = subtypeLocale.equals(systemLocale); mIsSystemLanguage = mIsSystemLocale || subtypeLocale.startsWith(systemLocale.substring(0, 2)); } } @Override public int compareTo(ImeSubtypeListItem other) { if (TextUtils.isEmpty(mImeName)) { return 1; } if (TextUtils.isEmpty(other.mImeName)) { return -1; } if (!TextUtils.equals(mImeName, other.mImeName)) { return mImeName.toString().compareTo(other.mImeName.toString()); } if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) { return 0; } if (mIsSystemLocale) { return -1; } if (other.mIsSystemLocale) { return 1; } if (mIsSystemLanguage) { return -1; } if (other.mIsSystemLanguage) { return 1; } if (TextUtils.isEmpty(mSubtypeName)) { return 1; } if (TextUtils.isEmpty(other.mSubtypeName)) { return -1; } return mSubtypeName.toString().compareTo(other.mSubtypeName.toString()); } } private static class InputMethodAndSubtypeCircularList { private final Context mContext; // Used to load label private final PackageManager mPm; private final String mSystemLocaleStr; private final InputMethodSettings mSettings; public InputMethodAndSubtypeCircularList(Context context, InputMethodSettings settings) { mContext = context; mSettings = settings; mPm = context.getPackageManager(); final Locale locale = context.getResources().getConfiguration().locale; mSystemLocaleStr = locale != null ? locale.toString() : ""; } private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis = new TreeMap<InputMethodInfo, List<InputMethodSubtype>>( new Comparator<InputMethodInfo>() { @Override public int compare(InputMethodInfo imi1, InputMethodInfo imi2) { if (imi2 == null) return 0; if (imi1 == null) return 1; if (mPm == null) { return imi1.getId().compareTo(imi2.getId()); } CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId(); CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId(); return imiId1.toString().compareTo(imiId2.toString()); } }); public ImeSubtypeListItem getNextInputMethod( boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { if (imi == null) { return null; } final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList(); if (imList.size() <= 1) { return null; } final int N = imList.size(); final int currentSubtypeId = subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode()) : NOT_A_SUBTYPE_ID; for (int i = 0; i < N; ++i) { final ImeSubtypeListItem isli = imList.get(i); if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) { if (!onlyCurrentIme) { return imList.get((i + 1) % N); } for (int j = 0; j < N - 1; ++j) { final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N); if (candidate.mImi.equals(imi)) { return candidate; } } return null; } } return null; } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() { return getSortedInputMethodAndSubtypeList(true, false, false); } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList( boolean showSubtypes, boolean inputShown, boolean isScreenLocked) { final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>(); final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis = mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked( mContext); if (immis == null || immis.size() == 0) { return Collections.emptyList(); } mSortedImmis.clear(); mSortedImmis.putAll(immis); for (InputMethodInfo imi : mSortedImmis.keySet()) { if (imi == null) { continue; } List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi); HashSet<String> enabledSubtypeSet = new HashSet<String>(); for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) { enabledSubtypeSet.add(String.valueOf(subtype.hashCode())); } final CharSequence imeLabel = imi.loadLabel(mPm); if (showSubtypes && enabledSubtypeSet.size() > 0) { final int subtypeCount = imi.getSubtypeCount(); if (DEBUG) { Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); } for (int j = 0; j < subtypeCount; ++j) { final InputMethodSubtype subtype = imi.getSubtypeAt(j); final String subtypeHashCode = String.valueOf(subtype.hashCode()); // We show all enabled IMEs and subtypes when an IME is shown. if (enabledSubtypeSet.contains(subtypeHashCode) && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) { final CharSequence subtypeLabel = subtype.overridesImplicitlyEnabledSubtype() ? null : subtype .getDisplayName(mContext, imi.getPackageName(), imi.getServiceInfo().applicationInfo); imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr)); // Removing this subtype from enabledSubtypeSet because we no // longer need to add an entry of this subtype to imList to avoid // duplicated entries. enabledSubtypeSet.remove(subtypeHashCode); } } } else { imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null, mSystemLocaleStr)); } } Collections.sort(imList); return imList; } } private final ArrayDeque<SubtypeParams> mTypedSubtypeHistory = new ArrayDeque<SubtypeParams>(); private final Object mLock = new Object(); private final InputMethodSettings mSettings; private InputMethodAndSubtypeCircularList mSubtypeList; public InputMethodSubtypeSwitchingController(InputMethodSettings settings) { mSettings = settings; } // TODO: write unit tests for this method and the logic that determines the next subtype public void onCommitText(InputMethodInfo imi, InputMethodSubtype subtype) { Loading @@ -62,4 +270,25 @@ public class InputMethodSubtypeSwitchingController { mTypedSubtypeHistory.addFirst(new SubtypeParams(imi, subtype)); } } public void resetCircularListLocked(Context context) { synchronized(mLock) { mSubtypeList = new InputMethodAndSubtypeCircularList(context, mSettings); } } public ImeSubtypeListItem getNextInputMethod( boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { synchronized(mLock) { return mSubtypeList.getNextInputMethod(onlyCurrentIme, imi, subtype); } } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes, boolean inputShown, boolean isScreenLocked) { synchronized(mLock) { return mSubtypeList.getSortedInputMethodAndSubtypeList( showSubtypes, inputShown, isScreenLocked); } } } core/java/com/android/internal/inputmethod/InputMethodUtils.java +11 −0 Original line number Diff line number Diff line Loading @@ -959,5 +959,16 @@ public class InputMethodUtils { addSubtypeToHistory(curMethodId, subtypeId); } } public HashMap<InputMethodInfo, List<InputMethodSubtype>> getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(Context context) { HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes = new HashMap<InputMethodInfo, List<InputMethodSubtype>>(); for (InputMethodInfo imi: getEnabledInputMethodListLocked()) { enabledInputMethodAndSubtypes.put( imi, getEnabledInputMethodSubtypeListLocked(context, imi, true)); } return enabledInputMethodAndSubtypes; } } } services/java/com/android/server/InputMethodManagerService.java +11 −201 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java +230 −1 Original line number Diff line number Diff line Loading @@ -16,11 +16,24 @@ package com.android.internal.inputmethod; import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings; import android.content.Context; import android.content.pm.PackageManager; import android.text.TextUtils; import android.util.Slog; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.TreeMap; /** * InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes. Loading @@ -29,10 +42,13 @@ public class InputMethodSubtypeSwitchingController { private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName(); private static final boolean DEBUG = false; private static final int MAX_HISTORY_SIZE = 4; private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; private static class SubtypeParams { public final InputMethodInfo mImi; public final InputMethodSubtype mSubtype; public final long mTime; public SubtypeParams(InputMethodInfo imi, InputMethodSubtype subtype) { mImi = imi; mSubtype = subtype; Loading @@ -40,7 +56,199 @@ public class InputMethodSubtypeSwitchingController { } } public static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> { public final CharSequence mImeName; public final CharSequence mSubtypeName; public final InputMethodInfo mImi; public final int mSubtypeId; private final boolean mIsSystemLocale; private final boolean mIsSystemLanguage; public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName, InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) { mImeName = imeName; mSubtypeName = subtypeName; mImi = imi; mSubtypeId = subtypeId; if (TextUtils.isEmpty(subtypeLocale)) { mIsSystemLocale = false; mIsSystemLanguage = false; } else { mIsSystemLocale = subtypeLocale.equals(systemLocale); mIsSystemLanguage = mIsSystemLocale || subtypeLocale.startsWith(systemLocale.substring(0, 2)); } } @Override public int compareTo(ImeSubtypeListItem other) { if (TextUtils.isEmpty(mImeName)) { return 1; } if (TextUtils.isEmpty(other.mImeName)) { return -1; } if (!TextUtils.equals(mImeName, other.mImeName)) { return mImeName.toString().compareTo(other.mImeName.toString()); } if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) { return 0; } if (mIsSystemLocale) { return -1; } if (other.mIsSystemLocale) { return 1; } if (mIsSystemLanguage) { return -1; } if (other.mIsSystemLanguage) { return 1; } if (TextUtils.isEmpty(mSubtypeName)) { return 1; } if (TextUtils.isEmpty(other.mSubtypeName)) { return -1; } return mSubtypeName.toString().compareTo(other.mSubtypeName.toString()); } } private static class InputMethodAndSubtypeCircularList { private final Context mContext; // Used to load label private final PackageManager mPm; private final String mSystemLocaleStr; private final InputMethodSettings mSettings; public InputMethodAndSubtypeCircularList(Context context, InputMethodSettings settings) { mContext = context; mSettings = settings; mPm = context.getPackageManager(); final Locale locale = context.getResources().getConfiguration().locale; mSystemLocaleStr = locale != null ? locale.toString() : ""; } private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis = new TreeMap<InputMethodInfo, List<InputMethodSubtype>>( new Comparator<InputMethodInfo>() { @Override public int compare(InputMethodInfo imi1, InputMethodInfo imi2) { if (imi2 == null) return 0; if (imi1 == null) return 1; if (mPm == null) { return imi1.getId().compareTo(imi2.getId()); } CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId(); CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId(); return imiId1.toString().compareTo(imiId2.toString()); } }); public ImeSubtypeListItem getNextInputMethod( boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { if (imi == null) { return null; } final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList(); if (imList.size() <= 1) { return null; } final int N = imList.size(); final int currentSubtypeId = subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode()) : NOT_A_SUBTYPE_ID; for (int i = 0; i < N; ++i) { final ImeSubtypeListItem isli = imList.get(i); if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) { if (!onlyCurrentIme) { return imList.get((i + 1) % N); } for (int j = 0; j < N - 1; ++j) { final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N); if (candidate.mImi.equals(imi)) { return candidate; } } return null; } } return null; } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() { return getSortedInputMethodAndSubtypeList(true, false, false); } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList( boolean showSubtypes, boolean inputShown, boolean isScreenLocked) { final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>(); final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis = mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked( mContext); if (immis == null || immis.size() == 0) { return Collections.emptyList(); } mSortedImmis.clear(); mSortedImmis.putAll(immis); for (InputMethodInfo imi : mSortedImmis.keySet()) { if (imi == null) { continue; } List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi); HashSet<String> enabledSubtypeSet = new HashSet<String>(); for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) { enabledSubtypeSet.add(String.valueOf(subtype.hashCode())); } final CharSequence imeLabel = imi.loadLabel(mPm); if (showSubtypes && enabledSubtypeSet.size() > 0) { final int subtypeCount = imi.getSubtypeCount(); if (DEBUG) { Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); } for (int j = 0; j < subtypeCount; ++j) { final InputMethodSubtype subtype = imi.getSubtypeAt(j); final String subtypeHashCode = String.valueOf(subtype.hashCode()); // We show all enabled IMEs and subtypes when an IME is shown. if (enabledSubtypeSet.contains(subtypeHashCode) && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) { final CharSequence subtypeLabel = subtype.overridesImplicitlyEnabledSubtype() ? null : subtype .getDisplayName(mContext, imi.getPackageName(), imi.getServiceInfo().applicationInfo); imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr)); // Removing this subtype from enabledSubtypeSet because we no // longer need to add an entry of this subtype to imList to avoid // duplicated entries. enabledSubtypeSet.remove(subtypeHashCode); } } } else { imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null, mSystemLocaleStr)); } } Collections.sort(imList); return imList; } } private final ArrayDeque<SubtypeParams> mTypedSubtypeHistory = new ArrayDeque<SubtypeParams>(); private final Object mLock = new Object(); private final InputMethodSettings mSettings; private InputMethodAndSubtypeCircularList mSubtypeList; public InputMethodSubtypeSwitchingController(InputMethodSettings settings) { mSettings = settings; } // TODO: write unit tests for this method and the logic that determines the next subtype public void onCommitText(InputMethodInfo imi, InputMethodSubtype subtype) { Loading @@ -62,4 +270,25 @@ public class InputMethodSubtypeSwitchingController { mTypedSubtypeHistory.addFirst(new SubtypeParams(imi, subtype)); } } public void resetCircularListLocked(Context context) { synchronized(mLock) { mSubtypeList = new InputMethodAndSubtypeCircularList(context, mSettings); } } public ImeSubtypeListItem getNextInputMethod( boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { synchronized(mLock) { return mSubtypeList.getNextInputMethod(onlyCurrentIme, imi, subtype); } } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes, boolean inputShown, boolean isScreenLocked) { synchronized(mLock) { return mSubtypeList.getSortedInputMethodAndSubtypeList( showSubtypes, inputShown, isScreenLocked); } } }
core/java/com/android/internal/inputmethod/InputMethodUtils.java +11 −0 Original line number Diff line number Diff line Loading @@ -959,5 +959,16 @@ public class InputMethodUtils { addSubtypeToHistory(curMethodId, subtypeId); } } public HashMap<InputMethodInfo, List<InputMethodSubtype>> getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(Context context) { HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes = new HashMap<InputMethodInfo, List<InputMethodSubtype>>(); for (InputMethodInfo imi: getEnabledInputMethodListLocked()) { enabledInputMethodAndSubtypes.put( imi, getEnabledInputMethodSubtypeListLocked(context, imi, true)); } return enabledInputMethodAndSubtypes; } } }
services/java/com/android/server/InputMethodManagerService.java +11 −201 File changed.Preview size limit exceeded, changes collapsed. Show changes