LatinIME/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java

251 lines
10 KiB
Java

/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.latin;
import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
import android.os.Build;
import android.util.Log;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.BuildCompatUtils;
import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.HashMap;
import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Enrichment class for InputMethodSubtype to enable concurrent multi-lingual input.
*
* Right now, this returns the extra value of its primary subtype.
*/
// non final for easy mocking.
public class RichInputMethodSubtype {
private static final String TAG = RichInputMethodSubtype.class.getSimpleName();
private static final HashMap<Locale, Locale> sLocaleMap = initializeLocaleMap();
private static final HashMap<Locale, Locale> initializeLocaleMap() {
final HashMap<Locale, Locale> map = new HashMap<>();
if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Locale#forLanguageTag is available on API Level 21+.
// TODO: Remove this workaround once when we become able to deal with "sr-Latn".
map.put(Locale.forLanguageTag("sr-Latn"), new Locale("sr_ZZ"));
}
return map;
}
@Nonnull
private final InputMethodSubtype mSubtype;
@Nonnull
private final Locale mLocale;
@Nonnull
private final Locale mOriginalLocale;
public RichInputMethodSubtype(@Nonnull final InputMethodSubtype subtype) {
mSubtype = subtype;
mOriginalLocale = InputMethodSubtypeCompatUtils.getLocaleObject(mSubtype);
final Locale mappedLocale = sLocaleMap.get(mOriginalLocale);
mLocale = mappedLocale != null ? mappedLocale : mOriginalLocale;
}
// Extra values are determined by the primary subtype. This is probably right, but
// we may have to revisit this later.
public String getExtraValueOf(@Nonnull final String key) {
return mSubtype.getExtraValueOf(key);
}
// The mode is also determined by the primary subtype.
public String getMode() {
return mSubtype.getMode();
}
public boolean isNoLanguage() {
return SubtypeLocaleUtils.NO_LANGUAGE.equals(mSubtype.getLocale());
}
public String getNameForLogging() {
return toString();
}
// InputMethodSubtype's display name for spacebar text in its locale.
// isAdditionalSubtype (T=true, F=false)
// locale layout | Middle Full
// ------ ------- - --------- ----------------------
// en_US qwerty F English English (US) exception
// en_GB qwerty F English English (UK) exception
// es_US spanish F Español Español (EE.UU.) exception
// fr azerty F Français Français
// fr_CA qwerty F Français Français (Canada)
// fr_CH swiss F Français Français (Suisse)
// de qwertz F Deutsch Deutsch
// de_CH swiss T Deutsch Deutsch (Schweiz)
// zz qwerty F QWERTY QWERTY
// fr qwertz T Français Français
// de qwerty T Deutsch Deutsch
// en_US azerty T English English (US)
// zz azerty T AZERTY AZERTY
// Get the RichInputMethodSubtype's full display name in its locale.
@Nonnull
public String getFullDisplayName() {
if (isNoLanguage()) {
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
}
return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(mSubtype.getLocale());
}
// Get the RichInputMethodSubtype's middle display name in its locale.
@Nonnull
public String getMiddleDisplayName() {
if (isNoLanguage()) {
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
}
return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(mSubtype.getLocale());
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof RichInputMethodSubtype)) {
return false;
}
final RichInputMethodSubtype other = (RichInputMethodSubtype)o;
return mSubtype.equals(other.mSubtype) && mLocale.equals(other.mLocale);
}
@Override
public int hashCode() {
return mSubtype.hashCode() + mLocale.hashCode();
}
@Override
public String toString() {
return "Multi-lingual subtype: " + mSubtype + ", " + mLocale;
}
@Nonnull
public Locale getLocale() {
return mLocale;
}
@Nonnull
public Locale getOriginalLocale() {
return mOriginalLocale;
}
public boolean isRtlSubtype() {
// The subtype is considered RTL if the language of the main subtype is RTL.
return LocaleUtils.isRtlLanguage(mLocale);
}
// TODO: remove this method
@Nonnull
public InputMethodSubtype getRawSubtype() { return mSubtype; }
@Nonnull
public String getKeyboardLayoutSetName() {
return SubtypeLocaleUtils.getKeyboardLayoutSetName(mSubtype);
}
public static RichInputMethodSubtype getRichInputMethodSubtype(
@Nullable final InputMethodSubtype subtype) {
if (subtype == null) {
return getNoLanguageSubtype();
} else {
return new RichInputMethodSubtype(subtype);
}
}
// Placeholer for no language QWERTY subtype. See {@link R.xml.method}.
private static final int SUBTYPE_ID_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
private static final String EXTRA_VALUE_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE =
"KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
+ "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
+ "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
@Nonnull
private static final RichInputMethodSubtype PLACEHOLDER_NO_LANGUAGE_SUBTYPE =
new RichInputMethodSubtype(InputMethodSubtypeCompatUtils.newInputMethodSubtype(
R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
EXTRA_VALUE_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE,
false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
SUBTYPE_ID_OF_PLACEHOLDER_NO_LANGUAGE_SUBTYPE));
// Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
// Placeholder Emoji subtype. See {@link R.xml.method}.
private static final int SUBTYPE_ID_OF_PLACEHOLDER_EMOJI_SUBTYPE = 0xd78b2ed0;
private static final String EXTRA_VALUE_OF_PLACEHOLDER_EMOJI_SUBTYPE =
"KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
+ "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
@Nonnull
private static final RichInputMethodSubtype PLACEHOLDER_EMOJI_SUBTYPE = new RichInputMethodSubtype(
InputMethodSubtypeCompatUtils.newInputMethodSubtype(
R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
EXTRA_VALUE_OF_PLACEHOLDER_EMOJI_SUBTYPE,
false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
SUBTYPE_ID_OF_PLACEHOLDER_EMOJI_SUBTYPE));
private static RichInputMethodSubtype sNoLanguageSubtype;
private static RichInputMethodSubtype sEmojiSubtype;
@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 placeholder subtype: "
+ PLACEHOLDER_NO_LANGUAGE_SUBTYPE);
return PLACEHOLDER_NO_LANGUAGE_SUBTYPE;
}
@Nonnull
public static RichInputMethodSubtype getEmojiSubtype() {
RichInputMethodSubtype emojiSubtype = sEmojiSubtype;
if (emojiSubtype == null) {
final InputMethodSubtype rawEmojiSubtype = RichInputMethodManager.getInstance()
.findSubtypeByLocaleAndKeyboardLayoutSet(
SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI);
if (rawEmojiSubtype != null) {
emojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype);
}
}
if (emojiSubtype != null) {
sEmojiSubtype = emojiSubtype;
return emojiSubtype;
}
Log.w(TAG, "Can't find emoji subtype");
Log.w(TAG, "No input method subtype found; returning placeholder subtype: "
+ PLACEHOLDER_EMOJI_SUBTYPE);
return PLACEHOLDER_EMOJI_SUBTYPE;
}
}