Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.inputmethod.latin;
     18 
     19 import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
     20 
     21 import android.util.Log;
     22 import android.view.inputmethod.InputMethodSubtype;
     23 
     24 import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
     25 import com.android.inputmethod.latin.common.Constants;
     26 import com.android.inputmethod.latin.common.LocaleUtils;
     27 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
     28 
     29 import java.util.Locale;
     30 
     31 import javax.annotation.Nonnull;
     32 import javax.annotation.Nullable;
     33 
     34 /**
     35  * Enrichment class for InputMethodSubtype to enable concurrent multi-lingual input.
     36  *
     37  * Right now, this returns the extra value of its primary subtype.
     38  */
     39 // non final for easy mocking.
     40 public class RichInputMethodSubtype {
     41     private static final String TAG = RichInputMethodSubtype.class.getSimpleName();
     42 
     43     @Nonnull
     44     private final InputMethodSubtype mSubtype;
     45     @Nonnull
     46     private final Locale mLocale;
     47 
     48     public RichInputMethodSubtype(@Nonnull final InputMethodSubtype subtype) {
     49         mSubtype = subtype;
     50         mLocale = LocaleUtils.constructLocaleFromString(mSubtype.getLocale());
     51     }
     52 
     53     // Extra values are determined by the primary subtype. This is probably right, but
     54     // we may have to revisit this later.
     55     public String getExtraValueOf(@Nonnull final String key) {
     56         return mSubtype.getExtraValueOf(key);
     57     }
     58 
     59     // The mode is also determined by the primary subtype.
     60     public String getMode() {
     61         return mSubtype.getMode();
     62     }
     63 
     64     public boolean isNoLanguage() {
     65         return SubtypeLocaleUtils.NO_LANGUAGE.equals(mSubtype.getLocale());
     66     }
     67 
     68     public String getNameForLogging() {
     69         return toString();
     70     }
     71 
     72     // InputMethodSubtype's display name for spacebar text in its locale.
     73     //        isAdditionalSubtype (T=true, F=false)
     74     // locale layout  |  Middle      Full
     75     // ------ ------- - --------- ----------------------
     76     //  en_US qwerty  F  English   English (US)           exception
     77     //  en_GB qwerty  F  English   English (UK)           exception
     78     //  es_US spanish F  Espaol   Espaol (EE.UU.)       exception
     79     //  fr    azerty  F  Franais  Franais
     80     //  fr_CA qwerty  F  Franais  Franais (Canada)
     81     //  fr_CH swiss   F  Franais  Franais (Suisse)
     82     //  de    qwertz  F  Deutsch   Deutsch
     83     //  de_CH swiss   T  Deutsch   Deutsch (Schweiz)
     84     //  zz    qwerty  F  QWERTY    QWERTY
     85     //  fr    qwertz  T  Franais  Franais
     86     //  de    qwerty  T  Deutsch   Deutsch
     87     //  en_US azerty  T  English   English (US)
     88     //  zz    azerty  T  AZERTY    AZERTY
     89     // Get the RichInputMethodSubtype's full display name in its locale.
     90     @Nonnull
     91     public String getFullDisplayName() {
     92         if (isNoLanguage()) {
     93             return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
     94         }
     95         return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(mSubtype.getLocale());
     96     }
     97 
     98     // Get the RichInputMethodSubtype's middle display name in its locale.
     99     @Nonnull
    100     public String getMiddleDisplayName() {
    101         if (isNoLanguage()) {
    102             return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
    103         }
    104         return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(mSubtype.getLocale());
    105     }
    106 
    107     @Override
    108     public boolean equals(final Object o) {
    109         if (!(o instanceof RichInputMethodSubtype)) {
    110             return false;
    111         }
    112         final RichInputMethodSubtype other = (RichInputMethodSubtype)o;
    113         return mSubtype.equals(other.mSubtype) && mLocale.equals(other.mLocale);
    114     }
    115 
    116     @Override
    117     public int hashCode() {
    118         return mSubtype.hashCode() + mLocale.hashCode();
    119     }
    120 
    121     @Override
    122     public String toString() {
    123         return "Multi-lingual subtype: " + mSubtype + ", " + mLocale;
    124     }
    125 
    126     @Nonnull
    127     public Locale getLocale() {
    128         return mLocale;
    129     }
    130 
    131     public boolean isRtlSubtype() {
    132         // The subtype is considered RTL if the language of the main subtype is RTL.
    133         return LocaleUtils.isRtlLanguage(mLocale);
    134     }
    135 
    136     // TODO: remove this method
    137     @Nonnull
    138     public InputMethodSubtype getRawSubtype() { return mSubtype; }
    139 
    140     @Nonnull
    141     public String getKeyboardLayoutSetName() {
    142         return SubtypeLocaleUtils.getKeyboardLayoutSetName(mSubtype);
    143     }
    144 
    145     public static RichInputMethodSubtype getRichInputMethodSubtype(
    146             @Nullable final InputMethodSubtype subtype) {
    147         if (subtype == null) {
    148             return getNoLanguageSubtype();
    149         } else {
    150             return new RichInputMethodSubtype(subtype);
    151         }
    152     }
    153 
    154     // Dummy no language QWERTY subtype. See {@link R.xml.method}.
    155     private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
    156     private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE =
    157             "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
    158             + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
    159             + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
    160             + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
    161     @Nonnull
    162     private static final RichInputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE =
    163             new RichInputMethodSubtype(InputMethodSubtypeCompatUtils.newInputMethodSubtype(
    164                     R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
    165                     SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
    166                     EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE,
    167                     false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
    168                     SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE));
    169     // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
    170     // Dummy Emoji subtype. See {@link R.xml.method}.
    171     private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0;
    172     private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE =
    173             "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
    174             + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
    175     @Nonnull
    176     private static final RichInputMethodSubtype DUMMY_EMOJI_SUBTYPE = new RichInputMethodSubtype(
    177             InputMethodSubtypeCompatUtils.newInputMethodSubtype(
    178                     R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
    179                     SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
    180                     EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE,
    181                     false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
    182                     SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE));
    183     private static RichInputMethodSubtype sNoLanguageSubtype;
    184     private static RichInputMethodSubtype sEmojiSubtype;
    185 
    186     @Nonnull
    187     public static RichInputMethodSubtype getNoLanguageSubtype() {
    188         RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype;
    189         if (noLanguageSubtype == null) {
    190             final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance()
    191                     .findSubtypeByLocaleAndKeyboardLayoutSet(
    192                             SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY);
    193             if (rawNoLanguageSubtype != null) {
    194                 noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype);
    195             }
    196         }
    197         if (noLanguageSubtype != null) {
    198             sNoLanguageSubtype = noLanguageSubtype;
    199             return noLanguageSubtype;
    200         }
    201         Log.w(TAG, "Can't find any language with QWERTY subtype");
    202         Log.w(TAG, "No input method subtype found; returning dummy subtype: "
    203                 + DUMMY_NO_LANGUAGE_SUBTYPE);
    204         return DUMMY_NO_LANGUAGE_SUBTYPE;
    205     }
    206 
    207     @Nonnull
    208     public static RichInputMethodSubtype getEmojiSubtype() {
    209         RichInputMethodSubtype emojiSubtype = sEmojiSubtype;
    210         if (emojiSubtype == null) {
    211             final InputMethodSubtype rawEmojiSubtype = RichInputMethodManager.getInstance()
    212                     .findSubtypeByLocaleAndKeyboardLayoutSet(
    213                             SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI);
    214             if (rawEmojiSubtype != null) {
    215                 emojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype);
    216             }
    217         }
    218         if (emojiSubtype != null) {
    219             sEmojiSubtype = emojiSubtype;
    220             return emojiSubtype;
    221         }
    222         Log.w(TAG, "Can't find emoji subtype");
    223         Log.w(TAG, "No input method subtype found; returning dummy subtype: "
    224                 + DUMMY_EMOJI_SUBTYPE);
    225         return DUMMY_EMOJI_SUBTYPE;
    226     }
    227 }
    228