static int readScriptId(final Resources resources, final InputMethodSubtype subtype) { final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); final int xmlId = getXmlId(resources, layoutSetName); final XmlResourceParser parser = resources.getXml(xmlId); try { while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { // Bovinate through the XML stupidly searching for TAG_FEATURE, and read // the script Id from it. parser.next(); final String tag = parser.getName(); if (TAG_FEATURE.equals(tag)) { return readScriptIdFromTagFeature(resources, parser); } } } catch (final IOException | XmlPullParserException e) { throw new RuntimeException(e.getMessage() + " in " + layoutSetName, e); } finally { parser.close(); } // If the tag is not found, then the default script is Latin. return ScriptUtils.SCRIPT_LATIN; }
public void setSubtype(final InputMethodSubtype subtype) { mPreviousSubtype = mSubtype; mSubtype = subtype; if (isIncomplete()) { setTitle(null); setDialogTitle(R.string.add_style); setKey(KEY_NEW_SUBTYPE); } else { final String displayName = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); setTitle(displayName); setDialogTitle(displayName); setKey(KEY_PREFIX + subtype.getLocale() + "_" + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype)); } }
@Override public void onSaveCustomInputStyle(final CustomInputStylePreference stylePref) { final InputMethodSubtype subtype = stylePref.getSubtype(); if (!stylePref.hasBeenModified()) { return; } if (findDuplicatedSubtype(subtype) == null) { mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); return; } // Saved subtype is duplicated. final PreferenceGroup group = getPreferenceScreen(); group.removePreference(stylePref); stylePref.revert(); group.addPreference(stylePref); showSubtypeAlreadyExistsToast(subtype); }
private void initInternal(final Context context) { if (isInitialized()) { return; } mImmWrapper = new InputMethodManagerCompatWrapper(context); mContext = context; mInputMethodInfoCache = new InputMethodInfoCache( mImmWrapper.mImm, context.getPackageName()); // Initialize additional subtypes. SubtypeLocaleUtils.init(context); final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(); mImmWrapper.mImm.setAdditionalInputMethodSubtypes( getInputMethodIdOfThisIme(), additionalSubtypes); // Initialize the current input method subtype and the shortcut IME. refreshSubtypeCaches(); }
private boolean switchToNextInputSubtypeInThisIme(final IBinder token, final boolean onlyCurrentIme) { final InputMethodManager imm = mImmWrapper.mImm; final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList( true /* allowsImplicitlySelectedSubtypes */); final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes); if (currentIndex == INDEX_NOT_FOUND) { Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype=" + SubtypeLocaleUtils.getSubtypeNameForLogging(currentSubtype)); return false; } final int nextIndex = (currentIndex + 1) % enabledSubtypes.size(); if (nextIndex <= currentIndex && !onlyCurrentIme) { // The current subtype is the last or only enabled one and it needs to switch to // next IME. return false; } final InputMethodSubtype nextSubtype = enabledSubtypes.get(nextIndex); setInputMethodAndSubtype(token, nextSubtype); return true; }
static void updateCustomInputStylesSummary(final Preference pref) { // When we are called from the Settings application but we are not already running, some // singleton and utility classes may not have been initialized. We have to call // initialization method of these classes here. See {@link LatinIME#onCreate()}. SubtypeLocaleUtils.init(pref.getContext()); final Resources res = pref.getContext().getResources(); final SharedPreferences prefs = pref.getSharedPreferences(); final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); final InputMethodSubtype[] subtypes = AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); final ArrayList<String> subtypeNames = new ArrayList<>(); for (final InputMethodSubtype subtype : subtypes) { subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); } // TODO: A delimiter of custom input styles should be localized. pref.setSummary(TextUtils.join(", ", subtypeNames)); }
@Nonnull public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) { String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET); if (keyboardLayoutSet == null) { // This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard // layout set in sLocaleAndExtraValueToKeyboardLayoutSetMap to keep it compatible with // pre-JellyBean. final String key = subtype.getLocale() + ":" + subtype.getExtraValue(); keyboardLayoutSet = sLocaleAndExtraValueToKeyboardLayoutSetMap.get(key); } // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is // fixed. if (keyboardLayoutSet == null) { Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " + "locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue()); return QWERTY; } return keyboardLayoutSet; }
@NonNull private static String getSubtypeDisplayNameInternal(@NonNull final InputMethodSubtype subtype, @NonNull final Locale displayLocale) { final String replacementString = getReplacementString(subtype, displayLocale); // TODO: rework this for multi-lingual subtypes final int nameResId = subtype.getNameResId(); final RunInLocale<String> getSubtypeName = new RunInLocale<String>() { @Override protected String job(final Resources res) { try { return res.getString(nameResId, replacementString); } catch (Resources.NotFoundException e) { // TODO: Remove this catch when InputMethodManager.getCurrentInputMethodSubtype // is fixed. Log.w(TAG, "Unknown subtype: mode=" + subtype.getMode() + " nameResId=" + subtype.getNameResId() + " locale=" + subtype.getLocale() + " extra=" + subtype.getExtraValue() + "\n" + DebugLogUtils.getStackTrace()); return ""; } } }; return StringUtils.capitalizeFirstCodePoint( getSubtypeName.runInLocale(sResources, displayLocale), displayLocale); }
@NonNull public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) { String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET); if (keyboardLayoutSet == null) { // This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard // layout set in sLocaleAndExtraValueToKeyboardLayoutSetMap to keep it compatible with // pre-JellyBean. final String key = subtype.getLocale() + ":" + subtype.getExtraValue(); keyboardLayoutSet = sLocaleAndExtraValueToKeyboardLayoutSetMap.get(key); } // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is // fixed. if (keyboardLayoutSet == null) { android.util.Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " + "locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue()); return QWERTY; } return keyboardLayoutSet; }
@Override public void onAddCustomInputStyle(final CustomInputStylePreference stylePref) { mIsAddingNewSubtype = false; final InputMethodSubtype subtype = stylePref.getSubtype(); if (findDuplicatedSubtype(subtype) == null) { mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); mSubtypePreferenceKeyForSubtypeEnabler = stylePref.getKey(); mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); return; } // Newly added subtype is duplicated. final PreferenceGroup group = getPreferenceScreen(); group.removePreference(stylePref); showSubtypeAlreadyExistsToast(subtype); }
public void switchSubtype(final IBinder token, final RichInputMethodManager richImm) { final InputMethodSubtype currentSubtype = richImm.getInputMethodManager() .getCurrentInputMethodSubtype(); final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype; final boolean currentSubtypeHasBeenUsed = mCurrentSubtypeHasBeenUsed; if (currentSubtypeHasBeenUsed) { mLastActiveSubtype = currentSubtype; mCurrentSubtypeHasBeenUsed = false; } if (currentSubtypeHasBeenUsed && richImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastActiveSubtype) && !currentSubtype.equals(lastActiveSubtype)) { richImm.setInputMethodAndSubtype(token, lastActiveSubtype); return; } richImm.switchToNextInputMethod(token, true /* onlyCurrentIme */); }
private static String getEnabledSubtypesLabel( Context context, InputMethodManager imm, InputMethodInfo imi) { if (context == null || imm == null || imi == null) return null; final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(imi, true); final StringBuilder sb = new StringBuilder(); final int N = subtypes.size(); for (int i = 0; i < N; ++i) { final InputMethodSubtype subtype = subtypes.get(i); if (sb.length() > 0) { sb.append(", "); } sb.append(subtype.getDisplayName(context, imi.getPackageName(), imi.getServiceInfo().applicationInfo)); } return sb.toString(); }
void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInput(editorInfo, restarting); // If the primary hint language does not match the current subtype language, then try // to switch to the primary hint language. // TODO: Support all the locales in EditorInfo#hintLocales. final Locale primaryHintLocale = EditorInfoCompatUtils.getPrimaryHintLocale(editorInfo); if (primaryHintLocale == null) { return; } final InputMethodSubtype newSubtype = mRichImm.findSubtypeByLocale(primaryHintLocale); if (newSubtype == null || newSubtype.equals(mRichImm.getCurrentSubtype().getRawSubtype())) { return; } mHandler.postSwitchLanguage(newSubtype); }
private static InputMethodSubtype createAdditionalSubtypeInternal( final String localeString, final String keyboardLayoutSetName, final boolean isAsciiCapable, final boolean isEmojiCapable) { final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName); final String platformVersionDependentExtraValues = getPlatformVersionDependentExtraValue( localeString, keyboardLayoutSetName, isAsciiCapable, isEmojiCapable); final int platformVersionIndependentSubtypeId = getPlatformVersionIndependentSubtypeId(localeString, keyboardLayoutSetName); // NOTE: In KitKat and later, InputMethodSubtypeBuilder#setIsAsciiCapable is also available. // TODO: Use InputMethodSubtypeBuilder#setIsAsciiCapable when appropriate. return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, platformVersionDependentExtraValues, false /* isAuxiliary */, false /* overrideImplicitlyEnabledSubtype */, platformVersionIndependentSubtypeId); }
@Nonnull public static RichInputMethodSubtype getNoLanguageSubtype() { RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype; if (noLanguageSubtype == null) { final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance() .findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY); if (rawNoLanguageSubtype != null) { noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype); } } if (noLanguageSubtype != null) { sNoLanguageSubtype = noLanguageSubtype; return noLanguageSubtype; } Log.w(TAG, "Can't find any language with QWERTY subtype"); Log.w(TAG, "No input method subtype found; returning dummy subtype: " + DUMMY_NO_LANGUAGE_SUBTYPE); return DUMMY_NO_LANGUAGE_SUBTYPE; }
private static boolean isAuxiliaryIme(final InputMethodInfo imi) { final int count = imi.getSubtypeCount(); if (count == 0) { return false; } for (int index = 0; index < count; index++) { final InputMethodSubtype subtype = imi.getSubtypeAt(index); if (!subtype.isAuxiliary()) { return false; } } return true; }
public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled( final InputMethodSubtype subtype) { final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype); final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(subtype, getMyEnabledInputMethodSubtypeList(false /* allowsImplicitlySelectedSubtypes */)); return subtypeEnabled && !subtypeExplicitlyEnabled; }
private static int getSubtypeIndexInList(final InputMethodSubtype subtype, final List<InputMethodSubtype> subtypes) { final int count = subtypes.size(); for (int index = 0; index < count; index++) { final InputMethodSubtype ims = subtypes.get(index); if (ims.equals(subtype)) { return index; } } return INDEX_NOT_FOUND; }
public void onSubtypeChanged(@NonNull final InputMethodSubtype newSubtype) { updateCurrentSubtype(newSubtype); updateShortcutIme(); if (DEBUG) { Log.w(TAG, "onSubtypeChanged: " + mCurrentRichInputMethodSubtype.getNameForLogging()); } }
public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) { mImmWrapper.mImm.setAdditionalInputMethodSubtypes( getInputMethodIdOfThisIme(), subtypes); // Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of // subtypes again next time. refreshSubtypeCaches(); }
private void updateShortcutIme() { if (DEBUG) { Log.d(TAG, "Update shortcut IME from : " + (mShortcutInputMethodInfo == null ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " + (mShortcutSubtype == null ? "<null>" : ( mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); } final RichInputMethodSubtype richSubtype = mCurrentRichInputMethodSubtype; final boolean implicitlyEnabledSubtype = checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled( richSubtype.getRawSubtype()); final Locale systemLocale = mContext.getResources().getConfiguration().locale; LanguageOnSpacebarUtils.onSubtypeChanged( richSubtype, implicitlyEnabledSubtype, systemLocale); LanguageOnSpacebarUtils.setEnabledSubtypes(getMyEnabledInputMethodSubtypeList( true /* allowsImplicitlySelectedSubtypes */)); // TODO: Update an icon for shortcut IME final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = getInputMethodManager().getShortcutInputMethodsAndSubtypes(); mShortcutInputMethodInfo = null; mShortcutSubtype = null; for (final InputMethodInfo imi : shortcuts.keySet()) { final List<InputMethodSubtype> subtypes = shortcuts.get(imi); // TODO: Returns the first found IMI for now. Should handle all shortcuts as // appropriate. mShortcutInputMethodInfo = imi; // TODO: Pick up the first found subtype for now. Should handle all subtypes // as appropriate. mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null; break; } if (DEBUG) { Log.d(TAG, "Update shortcut IME to : " + (mShortcutInputMethodInfo == null ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " + (mShortcutSubtype == null ? "<null>" : ( mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); } }
public static int getScriptId(final Resources resources, @Nonnull final InputMethodSubtype subtype) { final Integer value = sScriptIdsForSubtypes.get(subtype); if (null == value) { final int scriptId = Builder.readScriptId(resources, subtype); sScriptIdsForSubtypes.put(subtype, scriptId); return scriptId; } return value; }
public static RichInputMethodSubtype getRichInputMethodSubtype( @Nullable final InputMethodSubtype subtype) { if (subtype == null) { return getNoLanguageSubtype(); } else { return new RichInputMethodSubtype(subtype); } }
private void setPrefSubtypes(final String prefSubtypes, final Context context) { final PreferenceGroup group = getPreferenceScreen(); group.removeAll(); final InputMethodSubtype[] subtypesArray = AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtypes); for (final InputMethodSubtype subtype : subtypesArray) { final CustomInputStylePreference pref = new CustomInputStylePreference(context, subtype, this); group.addPreference(pref); } }
private static InputMethodSubtype createAdditionalSubtypeInternal( final String localeString, final String keyboardLayoutSetName, final boolean isAsciiCapable) { final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName); final String platformVersionDependentExtraValues = getPlatformVersionDependentExtraValue(keyboardLayoutSetName, isAsciiCapable); final int platformVersionIndependentSubtypeId = getPlatformVersionIndependentSubtypeId(localeString, keyboardLayoutSetName); // NOTE: In KitKat and later, InputMethodSubtypeBuilder#setIsAsciiCapable is also available. // TODO: Use InputMethodSubtypeBuilder#setIsAsciiCapable when appropriate. return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, platformVersionDependentExtraValues, false /* isAuxiliary */, false /* overrideImplicitlyEnabledSubtype */, platformVersionIndependentSubtypeId); }
public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) { if (TextUtils.isEmpty(prefSubtypes)) { return EMPTY_SUBTYPE_ARRAY; } final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR); final ArrayList<InputMethodSubtype> subtypesList = new ArrayList<>(prefSubtypeArray.length); for (final String prefSubtype : prefSubtypeArray) { final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR); if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE && elems.length != LENGTH_WITH_EXTRA_VALUE) { Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype + " in " + prefSubtypes); continue; } final String localeString = elems[INDEX_OF_LOCALE]; final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT]; // Here we assume that all the additional subtypes have AsciiCapable and EmojiCapable. // This is actually what the setting dialog for additional subtype is doing. final InputMethodSubtype subtype = createAsciiEmojiCapableAdditionalSubtype( localeString, keyboardLayoutSetName); if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) { // Skip unknown keyboard layout subtype. This may happen when predefined keyboard // layout has been removed. continue; } subtypesList.add(subtype); } return subtypesList.toArray(new InputMethodSubtype[subtypesList.size()]); }
public static String createPrefSubtypes(final InputMethodSubtype[] subtypes) { if (subtypes == null || subtypes.length == 0) { return ""; } final StringBuilder sb = new StringBuilder(); for (final InputMethodSubtype subtype : subtypes) { if (sb.length() > 0) { sb.append(PREF_SUBTYPE_SEPARATOR); } sb.append(getPrefSubtype(subtype)); } return sb.toString(); }
public static int getLanguageOnSpacebarFormatType( @NonNull final RichInputMethodSubtype subtype) { if (subtype.isNoLanguage()) { return FORMAT_TYPE_FULL_LOCALE; } // Only this subtype is enabled and equals to the system locale. if (sEnabledSubtypes.size() < 2 && sIsSystemLanguageSameAsInputLanguage) { return FORMAT_TYPE_NONE; } final Locale locale = subtype.getLocale(); if (locale == null) { return FORMAT_TYPE_NONE; } final String keyboardLanguage = locale.getLanguage(); final String keyboardLayout = subtype.getKeyboardLayoutSetName(); int sameLanguageAndLayoutCount = 0; for (final InputMethodSubtype ims : sEnabledSubtypes) { final String language = SubtypeLocaleUtils.getSubtypeLocale(ims).getLanguage(); if (keyboardLanguage.equals(language) && keyboardLayout.equals( SubtypeLocaleUtils.getKeyboardLayoutSetName(ims))) { sameLanguageAndLayoutCount++; } } // Display full locale name only when there are multiple subtypes that have the same // locale and keyboard layout. Otherwise displaying language name is enough. return sameLanguageAndLayoutCount > 1 ? FORMAT_TYPE_FULL_LOCALE : FORMAT_TYPE_LANGUAGE_ONLY; }
private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) { final Context context = getActivity(); final Resources res = context.getResources(); final String message = res.getString(R.string.custom_input_style_already_exists, SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); }
private InputMethodSubtype[] getSubtypes() { final PreferenceGroup group = getPreferenceScreen(); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); final int count = group.getPreferenceCount(); for (int i = 0; i < count; i++) { final Preference pref = group.getPreference(i); if (pref instanceof CustomInputStylePreference) { final CustomInputStylePreference subtypePref = (CustomInputStylePreference)pref; // We should not save newly adding subtype to preference because it is incomplete. if (subtypePref.isIncomplete()) continue; subtypes.add(subtypePref.getSubtype()); } } return subtypes.toArray(new InputMethodSubtype[subtypes.size()]); }
@Override public void onPause() { super.onPause(); final String oldSubtypes = Settings.readPrefAdditionalSubtypes(mPrefs, getResources()); final InputMethodSubtype[] subtypes = getSubtypes(); final String prefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(subtypes); if (DEBUG_CUSTOM_INPUT_STYLES) { Log.i(TAG, "Save custom input styles: " + prefSubtypes); } if (prefSubtypes.equals(oldSubtypes)) { return; } Settings.writePrefAdditionalSubtypes(mPrefs, prefSubtypes); mRichImm.setAdditionalInputMethodSubtypes(subtypes); }
public CustomInputStylePreference(final Context context, final InputMethodSubtype subtype, final Listener proxy) { super(context, null); setDialogLayoutResource(R.layout.additional_subtype_dialog); setPersistent(false); mProxy = proxy; setSubtype(subtype); }